diff --git a/infer/src/backend/crashcontext.ml b/infer/src/backend/crashcontext.ml index 54ffd7520..d2c3ce594 100644 --- a/infer/src/backend/crashcontext.ml +++ b/infer/src/backend/crashcontext.ml @@ -13,12 +13,14 @@ module F = Format module L = Logging let frame_id_of_stackframe frame = + let loc_str = match frame.Stacktrace.line_num with + | None -> frame.Stacktrace.file_str + | Some line -> F.sprintf "%s:%d" frame.Stacktrace.file_str line in F.sprintf - "%s.%s(%s:%d)" + "%s.%s(%s)" frame.Stacktrace.class_str frame.Stacktrace.method_str - frame.Stacktrace.file_str - frame.Stacktrace.line_num + loc_str let frame_id_of_summary stacktree = let short_name = IList.hd @@ -27,8 +29,10 @@ let frame_id_of_summary stacktree = | None -> failwith "Attempted to take signature of a frame without location \ information. This is undefined." - | Some loc -> - F.sprintf "%s(%s:%d)" short_name (Filename.basename loc.file) loc.line + | Some { line = Some line_num; file } -> + F.sprintf "%s(%s:%d)" short_name (Filename.basename file) line_num + | Some { file } -> + F.sprintf "%s(%s)" short_name (Filename.basename file) let stracktree_of_frame frame = { Stacktree_j.method_name = F.sprintf diff --git a/infer/src/checkers/BoundedCallTree.ml b/infer/src/checkers/BoundedCallTree.ml index baf2baa8c..38ecad745 100644 --- a/infer/src/checkers/BoundedCallTree.ml +++ b/infer/src/checkers/BoundedCallTree.ml @@ -61,7 +61,7 @@ let stacktree_of_pdesc let frame_loc = Some { Stacktree_j.location_type = location_type; file = DB.source_file_to_string loc.Location.file; - line = loc.Location.line; + line = Some loc.Location.line; blame_range = [line_range_of_pdesc pdesc] } in { Stacktree_j.method_name = Procname.to_unique_id procname; location = frame_loc; diff --git a/infer/src/checkers/stacktree.atd b/infer/src/checkers/stacktree.atd index d865859ac..9b325d434 100644 --- a/infer/src/checkers/stacktree.atd +++ b/infer/src/checkers/stacktree.atd @@ -6,7 +6,7 @@ type line_range_t = { type location_t = { location_type : string; file : string; - line : int; + ?line : int option; blame_range : line_range_t list; } diff --git a/infer/src/harness/stacktrace.ml b/infer/src/harness/stacktrace.ml index 4d401fa6d..0de241b30 100644 --- a/infer/src/harness/stacktrace.ml +++ b/infer/src/harness/stacktrace.ml @@ -17,7 +17,7 @@ type frame = { class_str : string; method_str : string; file_str : string; - line_num : int; + line_num : int option; } type t = { @@ -44,8 +44,11 @@ let make_frame class_str method_str file_str line_num = let frame_matches_location frame_obj loc = let lfname = DB.source_file_to_string loc.Location.file in - Utils.string_is_suffix frame_obj.file_str lfname && - frame_obj.line_num = loc.Location.line + let matches_file = Utils.string_is_suffix frame_obj.file_str lfname in + let matches_line = match frame_obj.line_num with + | None -> false + | Some line -> line = loc.Location.line in + matches_file && matches_line let parse_stack_frame frame_str = (* separate the qualified method name and the parenthesized text/line number*) @@ -58,18 +61,16 @@ let parse_stack_frame frame_str = let method_str = Str.matched_group 2 qualified_procname in (* Native methods don't have debugging info *) if string_equal file_and_line "Native Method" then - make_frame class_str method_str "Native Method" (-1) + make_frame class_str method_str "Native Method" None else begin (* Separate the filename and line number. note that a few methods might not have line number information, for those, file_and_line includes only the filename. *) let is_file_line = Str.string_match file_and_line_regexp file_and_line 0 in - let file_str = if is_file_line - then Str.matched_group 1 file_and_line - else file_and_line in - let line_num = if is_file_line - then int_of_string (Str.matched_group 2 file_and_line) - else -1 in + let file_str, line_num = if is_file_line + then Str.matched_group 1 file_and_line, + Some (int_of_string (Str.matched_group 2 file_and_line)) + else file_and_line, None in make_frame class_str method_str file_str line_num end diff --git a/infer/src/harness/stacktrace.mli b/infer/src/harness/stacktrace.mli index 71ccbb439..2f4bdc9c5 100644 --- a/infer/src/harness/stacktrace.mli +++ b/infer/src/harness/stacktrace.mli @@ -13,7 +13,7 @@ type frame = { class_str : string; method_str : string; file_str : string; - line_num : int; + line_num : int option; } type t = { @@ -23,7 +23,7 @@ type t = { val make : string -> frame list -> t -val make_frame : string -> string -> string -> int -> frame +val make_frame : string -> string -> string -> int option -> frame val frame_matches_location : frame -> Location.t -> bool diff --git a/infer/src/unit/BoundedCallTreeTests.ml b/infer/src/unit/BoundedCallTreeTests.ml index 221fc17ba..c32a423f1 100644 --- a/infer/src/unit/BoundedCallTreeTests.ml +++ b/infer/src/unit/BoundedCallTreeTests.ml @@ -28,8 +28,8 @@ let tests = let class_name = "com.example.SomeClass" in let file_name = "SomeClass.java" in let trace = Stacktrace.make "java.lang.NullPointerException" - [Stacktrace.make_frame class_name "foo" file_name 16; - Stacktrace.make_frame class_name "bar" file_name 20] in + [Stacktrace.make_frame class_name "foo" file_name (Some 16); + Stacktrace.make_frame class_name "bar" file_name (Some 20)] in let extras = { BoundedCallTree.get_proc_desc = mock_get_proc_desc; stacktrace = trace; } in let caller_foo_name = Procname.from_string_c_fun "foo" in diff --git a/infer/src/unit/stacktraceTests.ml b/infer/src/unit/stacktraceTests.ml index b56fa5747..654a1ac58 100644 --- a/infer/src/unit/stacktraceTests.ml +++ b/infer/src/unit/stacktraceTests.ml @@ -41,7 +41,7 @@ let tests = "endtoend.java.checkers.crashcontext.MinimalCrashTest" "main" "MinimalCrashTest.java" - 16] in + (Some 16)] in let one_frame_trace_test_ _ = assert_equal trace expected in "one_frame_trace">::one_frame_trace_test_ in @@ -62,9 +62,9 @@ let tests = let file_name = "MultiStackFrameCrashTest.java" in let expected = Stacktrace.make "java.lang.NullPointerException" - [Stacktrace.make_frame class_name "bar" file_name 16; - Stacktrace.make_frame class_name "foo" file_name 20; - Stacktrace.make_frame class_name "main" file_name 24] in + [Stacktrace.make_frame class_name "bar" file_name (Some 16); + Stacktrace.make_frame class_name "foo" file_name (Some 20); + Stacktrace.make_frame class_name "main" file_name (Some 24)] in let multi_frame_trace_test_ _ = assert_equal trace expected in "multi_frame_trace_test">::multi_frame_trace_test_ in @@ -81,7 +81,7 @@ let tests = "endtoend.java.checkers.crashcontext.MinimalCrashTest" "main" "MinimalCrashTest.java" - (-1)] in + None] in let missing_line_info_test_ _ = assert_equal trace expected in "missing_line_info_test">::missing_line_info_test_ in diff --git a/infer/tests/endtoend/java/crashcontext/NativeMethodTest.java b/infer/tests/endtoend/java/crashcontext/NativeMethodTest.java index 867a4d99a..e6ed3612c 100644 --- a/infer/tests/endtoend/java/crashcontext/NativeMethodTest.java +++ b/infer/tests/endtoend/java/crashcontext/NativeMethodTest.java @@ -45,7 +45,7 @@ public class NativeMethodTest { assertThat("The stack trace should contain " + FOO_METHOD, crashcontext.hasStackFrame(FOO_METHOD)); assertThat("The trace should contain at least one native method ", - crashcontext.hasLocationOnStack("Native Method", -1)); + crashcontext.hasNativeMethodOnStack()); assertThat("The stack trace should contain " + REFLECTION_INVOKE_METHOD, crashcontext.hasStackFrame(REFLECTION_INVOKE_METHOD)); assertThat("The stack trace should contain " + MAIN_METHOD, diff --git a/infer/tests/utils/CrashContextResults.java b/infer/tests/utils/CrashContextResults.java index 21be7339a..f75b4a206 100644 --- a/infer/tests/utils/CrashContextResults.java +++ b/infer/tests/utils/CrashContextResults.java @@ -59,6 +59,16 @@ public class CrashContextResults { return false; } + public boolean hasNativeMethodOnStack() { + for (JsonNode frame : json.path("stack")) { + if (frame.path("location").path("file").asText() + .equals("Native Method")) { + return true; + } + } + return false; + } + private List findNodesForMethod(JsonNode node, String methodSignature, List accumulator) {