diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index 5c9b94fa7..e2b7edcd3 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -243,34 +243,6 @@ let version_string = Unix.time *) let initial_analysis_time = Unix.time () -(* Splits a path into its parts. For example: - - (split "path/to/file") is [".", "path"; "to"; "file"] - - (split "/path/to/file") is ["/", "path"; "to"; "file"] *) -let split path = - let rec loop accu p = - match Filename.dirname p with - | d when d = p -> d :: accu - | d -> loop ((Filename.basename p) :: accu) d in - loop [] path - -(* Recursively resolve symlinks until we get something that is not a link. Executables may be - (multiple levels of) symbolic links to the real binary directory, eg after `make install` or - packaging. *) -let real_path path = - let rec resolve p = - match Unix.readlink p with - | link when Filename.is_relative link -> - (* [p] is a relative symbolic link *) - resolve ((Filename.dirname p) // link) - | link -> - (* [p] is an absolute symbolic link *) - resolve link - | exception Unix.Unix_error(Unix.EINVAL, _, _) -> - (* [p] is not a symbolic link *) - p in - IList.fold_left - (fun resolved_path p -> resolve (resolved_path // p)) "" (split path) - (* Resolve symlinks to get to the real executable. The real executable is located in [bin_dir] below, which allows us to find [lib_dir], [models_dir], etc., relative to it. *) let real_exe_name = diff --git a/infer/src/base/DB.ml b/infer/src/base/DB.ml index 66c8d1c4a..fd49e7ba6 100644 --- a/infer/src/base/DB.ml +++ b/infer/src/base/DB.ml @@ -81,6 +81,11 @@ let source_file_to_abs_path fname = | Relative path -> Filename.concat (project_root()) path | Absolute path -> path +let inode_equal sf1 sf2 = + let stat1 = Unix.stat (source_file_to_abs_path sf1) in + let stat2 = Unix.stat (source_file_to_abs_path sf2) in + stat1.st_dev = stat2.st_dev && stat1.st_ino = stat2.st_ino + let source_file_to_rel_path fname = match fname with | Relative path -> path diff --git a/infer/src/base/DB.mli b/infer/src/base/DB.mli index 679fc1f56..78e806b99 100644 --- a/infer/src/base/DB.mli +++ b/infer/src/base/DB.mli @@ -92,6 +92,9 @@ val source_file_compare : source_file -> source_file -> int (** equality of source files *) val source_file_equal : source_file -> source_file -> bool +(** equality of the files on disk *) +val inode_equal : source_file -> source_file -> bool + (** empty source file *) val source_file_empty : source_file diff --git a/infer/src/base/Utils.ml b/infer/src/base/Utils.ml index 104c44376..9c18ba185 100644 --- a/infer/src/base/Utils.ml +++ b/infer/src/base/Utils.ml @@ -677,3 +677,27 @@ let rec create_path path = | Unix.Unix_error (Unix.ENOENT, _, _) -> create_path (Filename.dirname path); create_dir path + +let real_path path = + (* Splits a path into its parts. For example: + - (split "path/to/file") is [".", "path"; "to"; "file"] + - (split "/path/to/file") is ["/", "path"; "to"; "file"] *) + let split path = + let rec loop accu p = + match Filename.dirname p with + | d when d = p -> d :: accu + | d -> loop ((Filename.basename p) :: accu) d in + loop [] path in + let rec resolve p = + match Unix.readlink p with + | link when Filename.is_relative link -> + (* [p] is a relative symbolic link *) + resolve ((Filename.dirname p) // link) + | link -> + (* [p] is an absolute symbolic link *) + resolve link + | exception Unix.Unix_error(Unix.EINVAL, _, _) -> + (* [p] is not a symbolic link *) + p in + IList.fold_left + (fun resolved_path p -> resolve (resolved_path // p)) "" (split path) diff --git a/infer/src/base/Utils.mli b/infer/src/base/Utils.mli index e51daf84d..a38b2477a 100644 --- a/infer/src/base/Utils.mli +++ b/infer/src/base/Utils.mli @@ -305,3 +305,8 @@ val create_dir : string -> unit (** [create_path path] will create a directory at [path], creating all the parent directories if non-existing *) val create_path : string -> unit + +(** Recursively resolve symlinks until we get something that is not a link. Executables may be + (multiple levels of) symbolic links to the real binary directory, eg after `make install` or + packaging. *) +val real_path : string -> string diff --git a/infer/src/clang/Capture.re b/infer/src/clang/Capture.re index 442fff485..22f85eb50 100644 --- a/infer/src/clang/Capture.re +++ b/infer/src/clang/Capture.re @@ -50,6 +50,7 @@ let init_global_state_for_capture_and_linters source_file => { ); register_perf_stats_report source_file; Config.curr_language := Config.Clang; + CLocation.curr_file := source_file; DB.Results_dir.init source_file; Clang_ast_main.reset_cache (); CFrontend_config.reset_global_state () diff --git a/infer/src/clang/cFrontend_utils.ml b/infer/src/clang/cFrontend_utils.ml index ffe38514a..204861c9e 100644 --- a/infer/src/clang/cFrontend_utils.ml +++ b/infer/src/clang/cFrontend_utils.ml @@ -441,9 +441,10 @@ struct let decl_info = Clang_ast_proj.get_decl_tuple decl in let file_opt = (fst decl_info.Clang_ast_t.di_source_range).Clang_ast_t.sl_file in match file_opt with - | None -> false + | None -> + false | Some file -> - DB.source_file_equal + DB.inode_equal (CLocation.source_file_from_path file) translation_unit_context.CFrontend_config.source_file diff --git a/infer/src/clang/cLocation.ml b/infer/src/clang/cLocation.ml index e4b2e8eff..3aae2bd96 100644 --- a/infer/src/clang/cLocation.ml +++ b/infer/src/clang/cLocation.ml @@ -31,7 +31,7 @@ let source_file_from_path path = let choose_sloc_to_update_curr_file trans_unit_ctx sloc1 sloc2 = match sloc2.Clang_ast_t.sl_file with - | Some f when DB.source_file_equal (source_file_from_path f) + | Some f when DB.inode_equal (source_file_from_path f) trans_unit_ctx.CFrontend_config.source_file -> sloc2 | _ -> sloc1 @@ -62,7 +62,7 @@ let clang_to_sil_location trans_unit_ctx clang_loc procdesc_opt = | Some f -> let file_db = source_file_from_path f in let nloc = - if (DB.source_file_equal file_db trans_unit_ctx.CFrontend_config.source_file) then + if (DB.inode_equal file_db trans_unit_ctx.CFrontend_config.source_file) then !Config.nLOC else -1 in file_db, nloc @@ -72,11 +72,13 @@ let clang_to_sil_location trans_unit_ctx clang_loc procdesc_opt = let file_in_project file = match Config.project_root with | Some root -> - let file_in_project = string_is_prefix root file in + let real_root = real_path root in + let real_file = real_path file in + let file_in_project = string_is_prefix real_root real_file in let paths = Config.skip_translation_headers in let file_should_be_skipped = IList.exists - (fun path -> string_is_prefix (Filename.concat root path) file) + (fun path -> string_is_prefix (Filename.concat real_root path) real_file) paths in file_in_project && not (file_should_be_skipped) | None -> false @@ -84,7 +86,8 @@ let file_in_project file = let should_do_frontend_check trans_unit_ctx (loc_start, _) = match loc_start.Clang_ast_t.sl_file with | Some file -> - let equal_current_source file = DB.source_file_equal (source_file_from_path file) + let equal_current_source file = + DB.inode_equal (source_file_from_path file) trans_unit_ctx.CFrontend_config.source_file in equal_current_source file || (file_in_project file && not Config.testing_mode) @@ -104,7 +107,7 @@ let should_translate trans_unit_ctx (loc_start, loc_end) decl_trans_context ~tra let path_pred path = pred (source_file_from_path path) in map_path_of path_pred loc in - let equal_current_source = DB.source_file_equal trans_unit_ctx.CFrontend_config.source_file + let equal_current_source = DB.inode_equal trans_unit_ctx.CFrontend_config.source_file in let file_in_project = map_path_of file_in_project loc_end || map_path_of file_in_project loc_start in