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 <dmalc...@redhat.com> --- 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