This adds support to sarif-replay to display fix-it hints
stored in GCC's SARIF output.
Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r15-7564-gd0d5204afff226.
gcc/ChangeLog:
* libsarifreplay.cc (sarif_replayer::handle_result_obj): Call
handle_fix_object if we see a single-element "fixes" array.
(sarif_replayer::handle_fix_object): New.
(sarif_replayer::handle_artifact_change_object): New.
gcc/testsuite/ChangeLog:
* sarif-replay.dg/2.1.0-valid/3.27.30-fixes-1.sarif: New test.
* sarif-replay.dg/2.1.0-valid/3.27.30-fixes-2.sarif: New test.
* sarif-replay.dg/2.1.0-valid/3.27.30-fixes-3.sarif: New test.
Signed-off-by: David Malcolm <[email protected]>
---
gcc/libsarifreplay.cc | 117 +++++++++++++++++-
.../2.1.0-valid/3.27.30-fixes-1.sarif | 55 ++++++++
.../2.1.0-valid/3.27.30-fixes-2.sarif | 39 ++++++
.../2.1.0-valid/3.27.30-fixes-3.sarif | 39 ++++++
4 files changed, 248 insertions(+), 2 deletions(-)
create mode 100644
gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.27.30-fixes-1.sarif
create mode 100644
gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.27.30-fixes-2.sarif
create mode 100644
gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.27.30-fixes-3.sarif
diff --git a/gcc/libsarifreplay.cc b/gcc/libsarifreplay.cc
index 21d8e6ce7cf..cc051dcd485 100644
--- a/gcc/libsarifreplay.cc
+++ b/gcc/libsarifreplay.cc
@@ -346,6 +346,16 @@ private:
lookup_rule_by_id_in_component (const char *rule_id,
const json::object &tool_component_obj);
+ // "fix" object (§3.55)
+ enum status
+ handle_fix_object (libgdiagnostics::diagnostic &diag,
+ const json::object &fix_obj);
+
+ // "artifactChange" object (§3.56)
+ enum status
+ handle_artifact_change_object (libgdiagnostics::diagnostic &diag,
+ const json::object &change_obj);
+
/* Support functions. */
/* Report an error to m_control_mgr about JV violating REF,
@@ -1012,7 +1022,6 @@ should_add_rule_p (const char *rule_id_str, const char
*url)
- doesn't yet handle "taxa" property (§3.27.8)
- handling of "level" property (§3.27.10) doesn't yet support the
full logic for when "level" is absent.
- - doesn't yet handle "fixes" property (§3.27.30)
- doesn't yet support multithreaded flows (§3.36.3)
*/
@@ -1200,6 +1209,17 @@ sarif_replayer::handle_result_obj (const json::object
&result_obj,
}
}
+ // §3.27.30 "fixes" property
+ const property_spec_ref prop_fixes ("result", "fixes", "3.27.30");
+ if (auto fixes_arr
+ = get_optional_property<json::array> (result_obj, prop_fixes))
+ {
+ // We only support a single fix
+ if (fixes_arr->length () == 1)
+ if (auto fix_obj = require_object (*fixes_arr->get (0), prop_fixes))
+ handle_fix_object (err, *fix_obj);
+ }
+
err.finish ("%s", text.get ());
// Flush any notes
@@ -1211,7 +1231,6 @@ sarif_replayer::handle_result_obj (const json::object
&result_obj,
}
return status::ok;
-
}
/* If ITER_SRC starts with a placeholder as per §3.11.5, advance ITER_SRC
@@ -2049,6 +2068,100 @@ lookup_rule_by_id_in_component (const char *rule_id,
return nullptr;
}
+// "fix" object (§3.55)
+
+enum status
+sarif_replayer::handle_fix_object (libgdiagnostics::diagnostic &diag,
+ const json::object &fix_obj)
+{
+ const property_spec_ref changes ("fix", "artifactChanges", "3.55.3");
+ auto changes_arr = get_required_property<json::array> (fix_obj, changes);
+ if (!changes_arr)
+ return status::err_invalid_sarif;
+
+ for (auto element : *changes_arr)
+ {
+ const json::object *change_obj
+ = require_object_for_element (*element, changes);
+ if (!change_obj)
+ return status::err_invalid_sarif;
+ enum status s = handle_artifact_change_object (diag, *change_obj);
+ if (s != status::ok)
+ return s;
+ }
+ return status::ok;
+}
+
+// "artifactChange" object (§3.56)
+
+enum status
+sarif_replayer::
+handle_artifact_change_object (libgdiagnostics::diagnostic &diag,
+ const json::object &change_obj)
+{
+ const property_spec_ref location
+ ("artifactChange", "artifactLocation", "3.56.2");
+ auto artifact_loc_obj
+ = get_required_property<json::object> (change_obj, location);
+ if (!artifact_loc_obj)
+ return status::err_invalid_sarif;
+
+ libgdiagnostics::file file;
+ enum status s = handle_artifact_location_object (*artifact_loc_obj, file);
+ if (s != status::ok)
+ return s;
+
+ const property_spec_ref replacements
+ ("artifactChange", "replacements", "3.56.3");
+ auto replacements_arr
+ = get_required_property<json::array> (change_obj, replacements);
+ if (!replacements_arr)
+ return status::err_invalid_sarif;
+ for (auto element : *replacements_arr)
+ {
+ // 3.57 replacement object
+ const json::object *replacement_obj
+ = require_object_for_element (*element, replacements);
+ if (!replacement_obj)
+ return status::err_invalid_sarif;
+
+ // 3.57.3 deletedRegion property
+ const property_spec_ref deleted_region
+ ("replacement", "deletedRegion", "3.57.3");
+ auto deleted_region_obj
+ = get_required_property<json::object> (*replacement_obj,
+ deleted_region);
+ if (!deleted_region_obj)
+ return status::err_invalid_sarif;
+
+ libgdiagnostics::physical_location phys_loc;
+ enum status s = handle_region_object (*deleted_region_obj,
+ file,
+ phys_loc);
+ if (s != status::ok)
+ return s;
+
+ // 3.57.4 insertedContent property
+ const property_spec_ref inserted_content
+ ("replacement", "insertedContent", "3.57.4");
+ const char *inserted_text = "";
+ if (auto inserted_content_obj
+ = get_optional_property<json::object> (*replacement_obj,
+ inserted_content))
+ {
+ const property_spec_ref prop_text
+ ("artifactContent", "text", "3.3.2");
+ if (auto text_jstr
+ = get_optional_property<json::string> (*inserted_content_obj,
+ prop_text))
+ inserted_text = text_jstr->get_string ();
+ }
+
+ diag.add_fix_it_hint_replace (phys_loc, inserted_text);
+ }
+ return status::ok;
+}
+
} // anonymous namespace
/* Error-checking at the API boundary. */
diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.27.30-fixes-1.sarif
b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.27.30-fixes-1.sarif
new file mode 100644
index 00000000000..c37411af75b
--- /dev/null
+++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.27.30-fixes-1.sarif
@@ -0,0 +1,55 @@
+/* Example of GCC SARIF output for a replacement fix-it hint. */
+
+{"$schema":
"https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json",
+ "version": "2.1.0",
+ "runs": [{"tool": {"driver": {"name": "GNU C23",
+ "fullName": "GNU C23 (GCC) version 15.0.1
20250203 (experimental) (x86_64-pc-linux-gnu)",
+ "version": "15.0.1 20250203 (experimental)",
+ "informationUri": "https://gcc.gnu.org/gcc-15/",
+ "rules": [{"id": "-Wformat=",
+ "helpUri":
"https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wformat"}]}},
+ "invocations": [{"executionSuccessful": true,
+ "toolExecutionNotifications": []}],
+ "originalUriBaseIds": {"PWD": {"uri":
"file:///this/path/does/not/exist/"}},
+ "artifacts": [{"location": {"uri":
"/this/path/does/not/exist/diagnostic-ranges.c",
+ "uriBaseId": "PWD"},
+ "sourceLanguage": "c",
+ "contents": {"text": "#include <stdio.h>\n\nvoid
test_mismatching_types (const char *msg)\n{\n printf(\"hello %i\",
msg);\n}\n"},
+ "roles": ["analysisTarget"]}],
+ "results": [{"ruleId": "-Wformat=",
+ "level": "warning",
+ "message": {"text": "format '%i' expects argument of
type 'int', but argument 2 has type 'const char *'"},
+ "locations": [{"physicalLocation":
{"artifactLocation": {"uri": "/this/path/does/not/exist/diagnostic-ranges.c",
+
"uriBaseId": "PWD"},
+ "region":
{"startLine": 5,
+
"startColumn": 17,
+
"endColumn": 19},
+ "contextRegion":
{"startLine": 5,
+
"snippet": {"text": " printf(\"hello %i\", msg);\n"}}},
+ "logicalLocations": [{"name":
"test_mismatching_types",
+
"fullyQualifiedName": "test_mismatching_types",
+ "decoratedName":
"test_mismatching_types",
+ "kind":
"function"}],
+ "annotations": [{"startLine": 5,
+ "startColumn": 17,
+ "endColumn": 19,
+ "message": {"text":
"int"}},
+ {"startLine": 5,
+ "startColumn": 22,
+ "endColumn": 25,
+ "message": {"text":
"const char *"}}]}],
+ "fixes": [{"artifactChanges": [{"artifactLocation":
{"uri": "/this/path/does/not/exist/diagnostic-ranges.c",
+
"uriBaseId": "PWD"},
+ "replacements":
[{"deletedRegion": {"startLine": 5,
+
"startColumn": 17,
+
"endColumn": 19},
+
"insertedContent": {"text": "%s"}}]}]}]}]}]}
+
+/* { dg-begin-multiline-output "" }
+/this/path/does/not/exist/diagnostic-ranges.c:5:17: warning: format '%i'
expects argument of type 'int', but argument 2 has type 'const char *'
[-Wformat=]
+ 5 | printf("hello %i", msg);
+ | ^~ ~~~
+ | | |
+ | int const char *
+ | %s
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.27.30-fixes-2.sarif
b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.27.30-fixes-2.sarif
new file mode 100644
index 00000000000..0334885447c
--- /dev/null
+++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.27.30-fixes-2.sarif
@@ -0,0 +1,39 @@
+/* Example of GCC SARIF output for a deletion fix-it hint. */
+
+{"$schema":
"https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json",
+ "version": "2.1.0",
+ "runs": [{"tool": {"driver": {"name": "GNU C++17",
+ "fullName": "GNU C++17 (GCC) version 15.0.1
20250203 (experimental) (x86_64-pc-linux-gnu)",
+ "version": "15.0.1 20250203 (experimental)",
+ "informationUri": "https://gcc.gnu.org/gcc-15/",
+ "rules": []}},
+ "invocations": [{"executionSuccessful": false,
+ "toolExecutionNotifications": []}],
+ "artifacts": [{"location": {"uri": "t.cc",
+ "uriBaseId": "PWD"},
+ "sourceLanguage": "cplusplus",
+ "contents": {"text": "unsigned unsigned int i;\n"},
+ "roles": ["analysisTarget"]}],
+ "results": [{"ruleId": "error",
+ "level": "error",
+ "message": {"text": "duplicate 'unsigned'"},
+ "locations": [{"physicalLocation":
{"artifactLocation": {"uri": "t.cc",
+
"uriBaseId": "PWD"},
+ "region":
{"startLine": 1,
+
"startColumn": 10,
+
"endColumn": 18},
+ "contextRegion":
{"startLine": 1,
+
"snippet": {"text": "unsigned unsigned int i;\n"}}}}],
+ "fixes": [{"artifactChanges": [{"artifactLocation":
{"uri": "t.cc",
+
"uriBaseId": "PWD"},
+ "replacements":
[{"deletedRegion": {"startLine": 1,
+
"startColumn": 10,
+
"endColumn": 18},
+
"insertedContent": {"text": ""}}]}]}]}]}]}
+
+/* { dg-begin-multiline-output "" }
+t.cc:1:10: error: duplicate 'unsigned'
+ 1 | unsigned unsigned int i;
+ | ^~~~~~~~
+ | --------
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.27.30-fixes-3.sarif
b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.27.30-fixes-3.sarif
new file mode 100644
index 00000000000..d4e7d37a8e6
--- /dev/null
+++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.27.30-fixes-3.sarif
@@ -0,0 +1,39 @@
+/* Example of GCC SARIF output for an insertion fix-it hint. */
+
+{"$schema":
"https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json",
+ "version": "2.1.0",
+ "runs": [{"tool": {"driver": {"name": "GNU C23",
+ "fullName": "GNU C23 (GCC) version 15.0.1
20250203 (experimental) (x86_64-pc-linux-gnu)",
+ "version": "15.0.1 20250203 (experimental)",
+ "informationUri": "https://gcc.gnu.org/gcc-15/",
+ "rules": []}},
+ "invocations": [{"executionSuccessful": false,
+ "toolExecutionNotifications": []}],
+ "artifacts": [{"location": {"uri": "t.c",
+ "uriBaseId": "PWD"},
+ "sourceLanguage": "c",
+ "contents": {"text": "struct foo {};\n\nfoo
*ptr;\n"},
+ "roles": ["analysisTarget"]}],
+ "results": [{"ruleId": "error",
+ "level": "error",
+ "message": {"text": "unknown type name 'foo'; use
'struct' keyword to refer to the type"},
+ "locations": [{"physicalLocation":
{"artifactLocation": {"uri": "t.c",
+
"uriBaseId": "PWD"},
+ "region":
{"startLine": 3,
+
"startColumn": 1,
+
"endColumn": 4},
+ "contextRegion":
{"startLine": 3,
+
"snippet": {"text": "foo *ptr;\n"}}}}],
+ "fixes": [{"artifactChanges": [{"artifactLocation":
{"uri": "t.c",
+
"uriBaseId": "PWD"},
+ "replacements":
[{"deletedRegion": {"startLine": 3,
+
"startColumn": 1,
+
"endColumn": 1},
+
"insertedContent": {"text": "struct "}}]}]}]}]}]}
+
+/* { dg-begin-multiline-output "" }
+t.c:3:1: error: unknown type name 'foo'; use 'struct' keyword to refer to the
type
+ 3 | foo *ptr;
+ | ^~~
+ | struct
+ { dg-end-multiline-output "" } */
--
2.26.3