From: David Malcolm <dmalcolm@redhat.com>
To: gcc-patches@gcc.gnu.org
Subject: [PATCH 10/12] Add sarif frontend
Date: Wed, 22 Jun 2022 18:34:45 -0400 [thread overview]
Message-ID: <20220622223447.2462880-11-dmalcolm@redhat.com> (raw)
In-Reply-To: <20220622223447.2462880-1-dmalcolm@redhat.com>
This patch is a work-in-progress (lots of TODOs and FIXMEs) that adds a new
SARIF frontend to gcc: sarif-replayer, which is invoked when passing .sarif
files to the gcc driver program: it will attempt to replay the .sarif file
using the provided diagnostic formatting options.
gcc/ChangeLog:
* sarif/Make-lang.in: New file.
* sarif/lang.opt: New file.
* sarif/sarif-frontend.cc: New file.
* sarif/sarif-replay.cc: New file.
* sarif/sarif-replay.h: New file.
gcc/testsuite/ChangeLog:
* lib/sarif-dg.exp: New test.
* lib/sarif.exp: New test.
* sarif/bad-eval-with-code-flow.py: New test.
* sarif/escaped-braces.sarif: New test.
* sarif/invalid-json-array-missing-comma.sarif: New test.
* sarif/invalid-json-array-with-trailing-comma.sarif: New test.
* sarif/invalid-json-bad-token.sarif: New test.
* sarif/invalid-json-object-missing-comma.sarif: New test.
* sarif/invalid-json-object-with-trailing-comma.sarif: New test.
* sarif/invalid-sarif-bad-runs.sarif: New test.
* sarif/invalid-sarif-missing-arguments-for-placeholders.sarif:
New test.
* sarif/invalid-sarif-no-runs.sarif: New test.
* sarif/invalid-sarif-no-version.sarif: New test.
* sarif/invalid-sarif-non-object-in-runs.sarif: New test.
* sarif/invalid-sarif-not-an-object.sarif: New test.
* sarif/invalid-sarif-not-enough-arguments-for-placeholders.sarif:
New test.
* sarif/invalid-sarif-version-not-a-string.sarif: New test.
* sarif/malformed-placeholder.sarif: New test.
* sarif/null-runs.sarif: New test.
* sarif/roundtrip-signal-1.c.sarif: New test.
* sarif/sarif.exp: New test.
* sarif/signal-1.c.sarif: New test.
* sarif/spec-example-1.sarif: New test.
* sarif/spec-example-2.sarif: New test.
* sarif/spec-example-3.sarif: New test.
* sarif/spec-example-4.sarif: New test.
* sarif/tutorial-example-foo.sarif: New test.
Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
gcc/sarif/Make-lang.in | 132 ++
gcc/sarif/config-lang.in | 34 +
gcc/sarif/lang-specs.h | 26 +
gcc/sarif/lang.opt | 31 +
gcc/sarif/sarif-frontend.cc | 191 +++
gcc/sarif/sarif-replay.cc | 1489 +++++++++++++++++
gcc/sarif/sarif-replay.h | 26 +
gcc/testsuite/lib/sarif-dg.exp | 233 +++
gcc/testsuite/lib/sarif.exp | 36 +
.../sarif/bad-eval-with-code-flow.py | 10 +
gcc/testsuite/sarif/escaped-braces.sarif | 19 +
.../invalid-json-array-missing-comma.sarif | 6 +
...valid-json-array-with-trailing-comma.sarif | 6 +
.../sarif/invalid-json-bad-token.sarif | 6 +
.../invalid-json-object-missing-comma.sarif | 7 +
...alid-json-object-with-trailing-comma.sarif | 6 +
.../sarif/invalid-sarif-bad-runs.sarif | 7 +
...f-missing-arguments-for-placeholders.sarif | 14 +
.../sarif/invalid-sarif-no-runs.sarif | 6 +
.../sarif/invalid-sarif-no-version.sarif | 6 +
.../invalid-sarif-non-object-in-runs.sarif | 7 +
.../sarif/invalid-sarif-not-an-object.sarif | 6 +
...ot-enough-arguments-for-placeholders.sarif | 14 +
.../invalid-sarif-version-not-a-string.sarif | 6 +
.../sarif/malformed-placeholder.sarif | 15 +
gcc/testsuite/sarif/null-runs.sarif | 2 +
.../sarif/roundtrip-signal-1.c.sarif | 398 +++++
gcc/testsuite/sarif/sarif.exp | 50 +
gcc/testsuite/sarif/signal-1.c.sarif | 362 ++++
gcc/testsuite/sarif/spec-example-1.sarif | 15 +
gcc/testsuite/sarif/spec-example-2.sarif | 74 +
gcc/testsuite/sarif/spec-example-3.sarif | 67 +
gcc/testsuite/sarif/spec-example-4.sarif | 758 +++++++++
.../sarif/tutorial-example-foo.sarif | 117 ++
34 files changed, 4182 insertions(+)
create mode 100644 gcc/sarif/Make-lang.in
create mode 100644 gcc/sarif/config-lang.in
create mode 100644 gcc/sarif/lang-specs.h
create mode 100644 gcc/sarif/lang.opt
create mode 100644 gcc/sarif/sarif-frontend.cc
create mode 100644 gcc/sarif/sarif-replay.cc
create mode 100644 gcc/sarif/sarif-replay.h
create mode 100644 gcc/testsuite/lib/sarif-dg.exp
create mode 100644 gcc/testsuite/lib/sarif.exp
create mode 100644 gcc/testsuite/sarif/bad-eval-with-code-flow.py
create mode 100644 gcc/testsuite/sarif/escaped-braces.sarif
create mode 100644 gcc/testsuite/sarif/invalid-json-array-missing-comma.sarif
create mode 100644 gcc/testsuite/sarif/invalid-json-array-with-trailing-comma.sarif
create mode 100644 gcc/testsuite/sarif/invalid-json-bad-token.sarif
create mode 100644 gcc/testsuite/sarif/invalid-json-object-missing-comma.sarif
create mode 100644 gcc/testsuite/sarif/invalid-json-object-with-trailing-comma.sarif
create mode 100644 gcc/testsuite/sarif/invalid-sarif-bad-runs.sarif
create mode 100644 gcc/testsuite/sarif/invalid-sarif-missing-arguments-for-placeholders.sarif
create mode 100644 gcc/testsuite/sarif/invalid-sarif-no-runs.sarif
create mode 100644 gcc/testsuite/sarif/invalid-sarif-no-version.sarif
create mode 100644 gcc/testsuite/sarif/invalid-sarif-non-object-in-runs.sarif
create mode 100644 gcc/testsuite/sarif/invalid-sarif-not-an-object.sarif
create mode 100644 gcc/testsuite/sarif/invalid-sarif-not-enough-arguments-for-placeholders.sarif
create mode 100644 gcc/testsuite/sarif/invalid-sarif-version-not-a-string.sarif
create mode 100644 gcc/testsuite/sarif/malformed-placeholder.sarif
create mode 100644 gcc/testsuite/sarif/null-runs.sarif
create mode 100644 gcc/testsuite/sarif/roundtrip-signal-1.c.sarif
create mode 100644 gcc/testsuite/sarif/sarif.exp
create mode 100644 gcc/testsuite/sarif/signal-1.c.sarif
create mode 100644 gcc/testsuite/sarif/spec-example-1.sarif
create mode 100644 gcc/testsuite/sarif/spec-example-2.sarif
create mode 100644 gcc/testsuite/sarif/spec-example-3.sarif
create mode 100644 gcc/testsuite/sarif/spec-example-4.sarif
create mode 100644 gcc/testsuite/sarif/tutorial-example-foo.sarif
diff --git a/gcc/sarif/Make-lang.in b/gcc/sarif/Make-lang.in
new file mode 100644
index 00000000000..53b6239da07
--- /dev/null
+++ b/gcc/sarif/Make-lang.in
@@ -0,0 +1,132 @@
+# Make-lang.in -- Top level -*- makefile -*- fragment for gcc SARIF "frontend".
+
+# Copyright (C) 2022 Free Software Foundation, Inc.
+
+# This file is part of GCC.
+
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# This file provides the language dependent support in the main Makefile.
+
+# The name for selecting sarif in LANGUAGES.
+sarif: sarif-replay$(exeext)
+
+.PHONY: sarif
+
+SARIF_OBJS = sarif/sarif-frontend.o sarif/sarif-replay.o \
+ attribs.o deferred-locations.o json-reader.o
+sarif_OBJS = $(SARIF_OBJS)
+
+sarif-replay$(exeext): $(SARIF_OBJS) $(BACKEND) $(LIBDEPS)
+ @$(call LINK_PROGRESS,$(INDEX.sarif),start)
+ +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \
+ $(SARIF_OBJS) $(BACKEND) $(LIBS) $(BACKENDLIBS)
+ @$(call LINK_PROGRESS,$(INDEX.sarif),end)
+
+# Build hooks.
+
+sarif.all.cross:
+sarif.start.encap:
+sarif.rest.encap:
+
+sarif.info:
+sarif.man:
+
+lang_checks += check-sarif
+#lang_checks_parallelized += check-sarif
+#check_sarif_parallelize = 10
+
+# No sarif-specific selftests
+selftest-sarif:
+
+# Install hooks.
+
+sarif.install-common: installdirs
+ -rm -f $(DESTDIR)$(bindir)/$(GCCSARIF_INSTALL_NAME)$(exeext)
+ $(INSTALL_PROGRAM) gccsarif$(exeext) $(DESTDIR)$(bindir)/$(GCCSARIF_INSTALL_NAME)$(exeext)
+ -if test -f sarif-replay$(exeext); then \
+ if test -f gccsarif-cross$(exeext); then \
+ :; \
+ else \
+ rm -f $(DESTDIR)$(bindir)/$(GCCSARIF_TARGET_INSTALL_NAME)$(exeext); \
+ ( cd $(DESTDIR)$(bindir) && \
+ $(LN) $(GCCSARIF_INSTALL_NAME)$(exeext) $(GCCSARIF_TARGET_INSTALL_NAME)$(exeext) ); \
+ fi; \
+ fi
+
+sarif.install-plugin:
+
+sarif.install-info: $(DESTDIR)$(infodir)/gccsarif.info
+
+sarif.install-pdf: doc/gccsarif.pdf
+ @$(NORMAL_INSTALL)
+ test -z "$(pdfdir)" || $(mkinstalldirs) "$(DESTDIR)$(pdfdir)/gcc"
+ @for p in doc/gccsarif.pdf; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ f=$(pdf__strip_dir) \
+ echo " $(INSTALL_DATA) '$$d$$p' '$(DESTDIR)$(pdfdir)/gcc/$$f'"; \
+ $(INSTALL_DATA) "$$d$$p" "$(DESTDIR)$(pdfdir)/gcc/$$f"; \
+ done
+
+sarif.install-html: $(build_htmldir)/sarif
+ @$(NORMAL_INSTALL)
+ test -z "$(htmldir)" || $(mkinstalldirs) "$(DESTDIR)$(htmldir)"
+ @for p in $(build_htmldir)/sarif; do \
+ if test -f "$$p" || test -d "$$p"; then d=""; else d="$(srcdir)/"; fi; \
+ f=$(html__strip_dir) \
+ if test -d "$$d$$p"; then \
+ echo " $(mkinstalldirs) '$(DESTDIR)$(htmldir)/$$f'"; \
+ $(mkinstalldirs) "$(DESTDIR)$(htmldir)/$$f" || exit 1; \
+ echo " $(INSTALL_DATA) '$$d$$p'/* '$(DESTDIR)$(htmldir)/$$f'"; \
+ $(INSTALL_DATA) "$$d$$p"/* "$(DESTDIR)$(htmldir)/$$f"; \
+ else \
+ echo " $(INSTALL_DATA) '$$d$$p' '$(DESTDIR)$(htmldir)/$$f'"; \
+ $(INSTALL_DATA) "$$d$$p" "$(DESTDIR)$(htmldir)/$$f"; \
+ fi; \
+ done
+
+sarif.install-man: $(DESTDIR)$(man1dir)/$(GCCSARIF_INSTALL_NAME)$(man1ext)
+
+sarif.uninstall:
+ rm -rf $(DESTDIR)$(bindir)/$(GCCSARIF_INSTALL_NAME)$(exeext)
+ rm -rf $(DESTDIR)$(man1dir)/$(GCCSARIF_INSTALL_NAME)$(man1ext)
+ rm -rf $(DESTDIR)$(bindir)/$(GCCSARIF_TARGET_INSTALL_NAME)$(exeext)
+ rm -rf $(DESTDIR)$(infodir)/gccsarif.info*
+
+# Clean hooks.
+
+sarif.mostlyclean:
+ -rm -f sarif/*$(objext)
+ -rm -f sarif/*$(coverageexts)
+ -rm -f gccsarif$(exeext) gccsarif-cross$(exeext) sarif-replay$(exeext)
+sarif.clean:
+sarif.distclean:
+sarif.maintainer-clean:
+ -rm -f $(docobjdir)/gccsarif.1
+
+# Stage hooks.
+
+sarif.stage1: stage1-start
+ -mv sarif/*$(objext) stage1/sarif
+sarif.stage2: stage2-start
+ -mv sarif/*$(objext) stage2/sarif
+sarif.stage3: stage3-start
+ -mv sarif/*$(objext) stage3/sarif
+sarif.stage4: stage4-start
+ -mv sarif/*$(objext) stage4/sarif
+sarif.stageprofile: stageprofile-start
+ -mv sarif/*$(objext) stageprofile/sarif
+sarif.stagefeedback: stagefeedback-start
+ -mv sarif/*$(objext) stagefeedback/sarif
diff --git a/gcc/sarif/config-lang.in b/gcc/sarif/config-lang.in
new file mode 100644
index 00000000000..6ed01f4116d
--- /dev/null
+++ b/gcc/sarif/config-lang.in
@@ -0,0 +1,34 @@
+# config-lang.in -- Top level configure fragment for gcc SARIF "frontend".
+
+# Copyright (C) 2022 Free Software Foundation, Inc.
+
+# This file is part of GCC.
+
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# Configure looks for the existence of this file to auto-config each language.
+# We define several parameters used by configure:
+#
+# language - name of language as it would appear in $(LANGUAGES)
+# compilers - value to add to $(COMPILERS)
+
+language="sarif"
+
+compilers="sarif-replay\$(exeext)"
+
+gtfiles="\$(srcdir)/sarif/sarif-frontend.cc"
+
+# Build by default.
+build_by_default="yes"
diff --git a/gcc/sarif/lang-specs.h b/gcc/sarif/lang-specs.h
new file mode 100644
index 00000000000..750689c8374
--- /dev/null
+++ b/gcc/sarif/lang-specs.h
@@ -0,0 +1,26 @@
+/* lang-specs.h -- gcc driver specs for the SARIF "frontend".
+ Copyright (C) 2022 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+/* This is the contribution to the `default_compilers' array in gcc.cc
+ for the sarif "frontend". */
+
+{".sarif", "@sarif", 0, 1, 0},
+/* FIXME: */
+{"@sarif", "sarif-replay %i %(cc1_options)",
+ 0, 1, 0},
diff --git a/gcc/sarif/lang.opt b/gcc/sarif/lang.opt
new file mode 100644
index 00000000000..33d17879333
--- /dev/null
+++ b/gcc/sarif/lang.opt
@@ -0,0 +1,31 @@
+; Options for the SARIF front end.
+; Copyright (C) 2022 Free Software Foundation, Inc.
+;
+; This file is part of GCC.
+;
+; GCC is free software; you can redistribute it and/or modify it under
+; the terms of the GNU General Public License as published by the Free
+; Software Foundation; either version 3, or (at your option) any later
+; version.
+;
+; GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+; WARRANTY; without even the implied warranty of MERCHANTABILITY or
+; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+; for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with GCC; see the file COPYING3. If not see
+; <http://www.gnu.org/licenses/>.
+
+; See the GCC internals manual for a description of this file's format.
+
+; Please try to keep this file in ASCII collating order.
+
+Language
+SARIF
+
+fallow-comments
+SARIF Var(flag_allow_comments)
+Extend JSON to support comments
+
+; This comment is to ensure we retain the blank line above.
diff --git a/gcc/sarif/sarif-frontend.cc b/gcc/sarif/sarif-frontend.cc
new file mode 100644
index 00000000000..7623c85fee4
--- /dev/null
+++ b/gcc/sarif/sarif-frontend.cc
@@ -0,0 +1,191 @@
+/* The dummy "frontend" for re-emitting diagnostics saved in SARIF form.
+ Copyright (C) 2022 Free Software Foundation, Inc.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+// TODO: prune this
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "debug.h"
+#include "langhooks.h"
+#include "langhooks-def.h"
+#include "diagnostic.h"
+#include "diagnostic-metadata.h"
+#include "diagnostic-path.h"
+#include "opts.h"
+#include "options.h"
+#include "line-map.h"
+#include "stringpool.h"
+#include "gcc-rich-location.h"
+#include "json.h"
+#include "json-reader.h"
+#include "deferred-locations.h"
+#include "logical-location.h"
+#include "diagnostic-client-data-hooks.h"
+#include "sarif/sarif-replay.h"
+
+/* Placeholder implementation; needed by a frontend. */
+
+tree
+convert (tree, tree)
+{
+ gcc_unreachable ();
+ return NULL_TREE;
+}
+
+/* Language-dependent contents of a type. */
+
+struct GTY(()) lang_type
+{
+ char dummy;
+};
+
+/* Language-dependent contents of a decl. */
+
+struct GTY((variable_size)) lang_decl
+{
+ char dummy;
+};
+
+/* Language-dependent contents of an identifier. This must include a
+ tree_identifier. */
+
+struct GTY(()) lang_identifier
+{
+ struct tree_identifier common;
+};
+
+/* The resulting tree type. */
+
+union GTY((desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"),
+ chain_next ("CODE_CONTAINS_STRUCT (TREE_CODE (&%h.generic), TS_COMMON) ? ((union lang_tree_node *) TREE_CHAIN (&%h.generic)) : NULL")))
+lang_tree_node
+{
+ union tree_node GTY((tag ("0"),
+ desc ("tree_node_structure (&%h)"))) generic;
+ struct lang_identifier GTY((tag ("1"))) identifier;
+};
+
+/* We don't use language_function. */
+
+struct GTY(()) language_function
+{
+ int dummy;
+};
+
+/* Language hooks. */
+
+static bool
+sarif_langhook_init (void)
+{
+ build_common_tree_nodes (false);
+
+ replay_sarif (main_input_filename);
+
+ return false;
+}
+
+static unsigned int
+sarif_langhook_option_lang_mask (void)
+{
+ return CL_SARIF;
+}
+
+static bool
+sarif_langhook_handle_option (size_t scode,
+ const char *arg,
+ HOST_WIDE_INT value,
+ int kind,
+ location_t loc,
+ const struct cl_option_handlers *handlers)
+{
+ bool result = true;
+
+ switch (scode)
+ {
+ default:
+ if (cl_options[scode].flags & sarif_langhook_option_lang_mask ())
+ break;
+ result = false;
+ }
+
+ SARIF_handle_option_auto (&global_options, &global_options_set,
+ scode, arg, value,
+ sarif_langhook_option_lang_mask (), kind,
+ loc, handlers, global_dc);
+
+ return result;
+}
+
+static tree
+sarif_langhook_type_for_mode (machine_mode, int)
+{
+ gcc_unreachable ();
+ return NULL_TREE;
+}
+
+static bool
+sarif_langhook_global_bindings_p (void)
+{
+ return true;
+}
+
+static tree
+sarif_langhook_pushdecl (tree decl ATTRIBUTE_UNUSED)
+{
+ gcc_unreachable ();
+}
+
+static tree
+sarif_langhook_getdecls (void)
+{
+ return NULL;
+}
+#undef LANG_HOOKS_NAME
+#define LANG_HOOKS_NAME "sarif"
+
+#undef LANG_HOOKS_INIT
+#define LANG_HOOKS_INIT sarif_langhook_init
+
+#undef LANG_HOOKS_OPTION_LANG_MASK
+#define LANG_HOOKS_OPTION_LANG_MASK sarif_langhook_option_lang_mask
+
+#undef LANG_HOOKS_HANDLE_OPTION
+#define LANG_HOOKS_HANDLE_OPTION sarif_langhook_handle_option
+
+#undef LANG_HOOKS_TYPE_FOR_MODE
+#define LANG_HOOKS_TYPE_FOR_MODE sarif_langhook_type_for_mode
+
+#undef LANG_HOOKS_GLOBAL_BINDINGS_P
+#define LANG_HOOKS_GLOBAL_BINDINGS_P sarif_langhook_global_bindings_p
+
+#undef LANG_HOOKS_PUSHDECL
+#define LANG_HOOKS_PUSHDECL sarif_langhook_pushdecl
+
+#undef LANG_HOOKS_GETDECLS
+#define LANG_HOOKS_GETDECLS sarif_langhook_getdecls
+
+#undef LANG_HOOKS_DEEP_UNSHARING
+#define LANG_HOOKS_DEEP_UNSHARING true
+
+struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
+
+#include "gt-sarif-sarif-frontend.h"
+#include "gtype-sarif.h"
diff --git a/gcc/sarif/sarif-replay.cc b/gcc/sarif/sarif-replay.cc
new file mode 100644
index 00000000000..2d5c58ead1e
--- /dev/null
+++ b/gcc/sarif/sarif-replay.cc
@@ -0,0 +1,1489 @@
+/* Re-emitting diagnostics saved in SARIF form.
+ Copyright (C) 2022 David Malcolm <dmalcolm@redhat.com>.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+// TODO: prune this
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "debug.h"
+#include "langhooks.h"
+#include "langhooks-def.h"
+#include "diagnostic.h"
+#include "diagnostic-metadata.h"
+#include "diagnostic-path.h"
+#include "opts.h"
+#include "options.h"
+#include "line-map.h"
+#include "stringpool.h"
+#include "gcc-rich-location.h"
+#include "json-reader.h"
+#include "deferred-locations.h"
+#include "logical-location.h"
+#include "diagnostic-client-data-hooks.h"
+#include "sarif/sarif-replay.h"
+
+/* Forward decls. */
+
+class sarif_replayer;
+class sarif_diagnostic_client_data_hooks;
+
+/* FIXME. */
+
+struct pending_location_range
+{
+ location_t m_start;
+ location_t m_end;
+};
+
+/* Concrete subclass of client_version_info, for use when
+ m_replayer->m_driver_obj is non-NULL. */
+
+class current_driver_version_info : public client_version_info
+{
+public:
+ current_driver_version_info (sarif_replayer *replayer)
+ : m_replayer (replayer)
+ {}
+
+ const char *get_tool_name () const final override;
+ char *maybe_make_full_name () const final override;
+ const char *get_version_string () const final override;
+ char *maybe_make_version_url () const final override;
+ void for_each_plugin (plugin_visitor &visitor) const final override;
+
+private:
+ sarif_replayer *m_replayer;
+};
+
+/* Concrete subclass of logical_location, for use when
+ m_replayer->m_current_logical_loc_obj is non-NULL. */
+
+class current_sarif_logical_location : public logical_location
+{
+public:
+ current_sarif_logical_location (sarif_replayer *replayer)
+ : m_replayer (replayer)
+ {}
+
+ const char *get_short_name () const final override;
+ const char *get_name_with_scope () const final override;
+ const char *get_internal_name () const final override;
+ enum logical_location_kind get_kind () const final override;
+
+private:
+ sarif_replayer *m_replayer;
+};
+
+/* Concrete subclass of diagnostic_client_data_hooks, for
+ use when replaying a SARIF file.
+
+ Takes ownership of the sarif_replayer and toplevel json::value objects. */
+
+class sarif_diagnostic_client_data_hooks : public diagnostic_client_data_hooks
+{
+ public:
+ sarif_diagnostic_client_data_hooks (sarif_replayer *replayer)
+ : m_replayer (replayer),
+ m_toplevel_jv (NULL),
+ m_driver_version_info (replayer),
+ m_current_logical_location (replayer)
+ {}
+
+ ~sarif_diagnostic_client_data_hooks ();
+
+ const client_version_info *get_any_version_info () const final override;
+ const logical_location *get_current_logical_location () const final override;
+ const char *
+ maybe_get_sarif_source_language (const char *filename) const final override;
+
+ void stash (json::value *toplevel_jv)
+ {
+ m_toplevel_jv = toplevel_jv;
+ }
+
+ sarif_replayer *m_replayer;
+ json::value *m_toplevel_jv;
+
+ current_driver_version_info m_driver_version_info;
+ current_sarif_logical_location m_current_logical_location;
+};
+
+/* Subclass of diagnostic_metadata::rule for a reference to the SARIF
+ specification. */
+
+class spec_ref : public diagnostic_metadata::rule
+{
+public:
+ spec_ref (const char *section)
+ : m_section (section)
+ {}
+
+ char *make_description () const final override
+ {
+ /* 'SECTION SIGN' (U+00A7). */
+#define SECTION_SIGN_UTF8 "\xC2\xA7"
+ return xasprintf ("SARIF v2.1.0 " SECTION_SIGN_UTF8 "%s", m_section);
+ }
+
+ char *make_url () const final override
+ {
+ /* There doesn't seem to be a systematic mapping from spec sections to
+ HTML anchors, so we can't provide URLs
+ (filed as https://github.com/oasis-tcs/sarif-spec/issues/533 ). */
+ return NULL;
+ }
+
+private:
+ /* e.g. "3.1" for section 3.1 of the spec. */
+ const char *m_section;
+};
+
+/* A reference to the SARIF specification for a particular kind of object. */
+
+class object_spec_ref : public spec_ref
+{
+public:
+ object_spec_ref (const char *obj_name, const char *section)
+ : spec_ref (section), m_obj_name (obj_name)
+ {}
+
+ const char *get_obj_name () const { return m_obj_name; }
+
+private:
+ const char *m_obj_name;
+};
+
+/* A reference to the SARIF specification for a particular property
+ of a particular kind of object. */
+
+class property_spec_ref : public object_spec_ref
+{
+public:
+ property_spec_ref (const char *obj_name,
+ const char *property_name,
+ const char *section)
+ : object_spec_ref (obj_name, section), m_property_name (property_name)
+ {}
+
+ const char *get_property_name () const { return m_property_name; }
+
+private:
+ const char *m_property_name;
+};
+
+/* A bundle of state for replaying a SARIF JSON file. */
+
+class sarif_replayer : public json_reader
+{
+public:
+ friend class current_driver_version_info;
+ friend class current_sarif_logical_location;
+ friend class sarif_diagnostic_client_data_hooks;
+
+ sarif_replayer (const char *filename);
+ ~sarif_replayer ();
+
+ json::value *
+ get_optional_property (json::object *obj, const property_spec_ref &ref)
+ {
+ return obj->get (ref.get_property_name ());
+ }
+
+ json::value *
+ get_required_property (json::object *obj, const property_spec_ref &ref)
+ {
+ json::value *property_val = get_optional_property (obj, ref);
+ if (!property_val)
+ fatal_error_with_ref (obj, ref,
+ "expected %s object to have a %qs property",
+ ref.get_obj_name (), ref.get_property_name ());
+ return property_val;
+ }
+
+ int
+ require_int (json::value *jv, const property_spec_ref &ref)
+ {
+ if (json::integer_number *num = dyn_cast <json::integer_number *> (jv))
+ return num->get ();
+ fatal_error_with_ref (jv, ref, "expected %s.%s to be an integer",
+ ref.get_obj_name (), ref.get_property_name ());
+ return 0;
+ }
+
+ bool
+ get_optional_int_property (int *out,
+ json::object *obj, const property_spec_ref &ref)
+ {
+ json::value *property_val = get_optional_property (obj, ref);
+ if (!property_val)
+ return false;
+ *out = require_int (property_val, ref);
+ return true;
+ }
+
+ const char *
+ require_string (json::value *jv, const property_spec_ref &ref)
+ {
+ json::string *str = dyn_cast <json::string *> (jv);
+ if (!str)
+ fatal_error_with_ref (jv, ref, "expected %s.%s to be a string",
+ ref.get_obj_name (), ref.get_property_name ());
+ return str->get_string ();
+ }
+
+ const char *
+ get_optional_string_property (json::object *obj, const property_spec_ref &ref)
+ {
+ json::value *property_val = get_optional_property (obj, ref);
+ if (!property_val)
+ return NULL;
+ return require_string (property_val, ref);
+ }
+
+ const char *
+ get_required_string_property (json::object *obj, const property_spec_ref &ref)
+ {
+ json::value *property_val = get_required_property (obj, ref);
+ return require_string (property_val, ref);
+ }
+
+ json::object *
+ require_object (json::value *jv, const property_spec_ref &ref)
+ {
+ if (json::object *obj = dyn_cast <json::object *> (jv))
+ return obj;
+ fatal_error_with_ref (jv, ref, "expected %s.%s to be an object",
+ ref.get_obj_name (), ref.get_property_name ());
+ return NULL;
+ }
+
+ json::object *
+ require_object_for_element (json::value *jv, const property_spec_ref &ref)
+ {
+ if (json::object *obj = dyn_cast <json::object *> (jv))
+ return obj;
+ fatal_error_with_ref (jv, ref,
+ "expected element of %s.%s array to be an object",
+ ref.get_obj_name (), ref.get_property_name ());
+ return NULL;
+ }
+
+ json::object *
+ get_optional_object_property (json::object *obj, const property_spec_ref &ref)
+ {
+ json::value *property_val = get_optional_property (obj, ref);
+ if (!property_val)
+ return NULL;
+ return require_object (property_val, ref);
+ }
+
+ json::object *
+ get_required_object_property (json::object *obj, const property_spec_ref &ref)
+ {
+ json::value *property_val = get_required_property (obj, ref);
+ if (!property_val)
+ return NULL;
+ return require_object (property_val, ref);
+ }
+
+ json::array *
+ require_array_property (json::value *jv, const property_spec_ref &ref)
+ {
+ if (json::array *obj = dyn_cast <json::array *> (jv))
+ return obj;
+ fatal_error_with_ref (jv, ref, "expected %s.%s to be an array",
+ ref.get_obj_name (), ref.get_property_name ());
+ return NULL;
+ }
+
+ json::array *
+ get_optional_array_property (json::object *obj, const property_spec_ref &ref)
+ {
+ json::value *property_val = get_optional_property (obj, ref);
+ if (!property_val)
+ return NULL;
+ return require_array_property (property_val, ref);
+ }
+
+ json::array *
+ get_required_array_property (json::object *obj, const property_spec_ref &ref)
+ {
+ json::value *property_val = get_required_property (obj, ref);
+ return require_array_property (property_val, ref);
+ }
+
+ void emit_sarif_as_diagnostics (json::value *jv, int pass);
+ void handle_run_obj (json::object *run_obj);
+ json::object *
+ lookup_rule_by_id_in_tool (const char *rule_id,
+ json::object *tool_obj);
+ json::object *
+ lookup_rule_by_id_in_component (const char *rule_id,
+ json::object *tool_component_obj);
+ diagnostic_path *
+ make_sarif_diagnostic_path (json::object *thread_flow_obj);
+ void handle_result_obj (json::object *result_obj,
+ json::object *tool_obj);
+
+ char *
+ make_plain_text_within_result_message (json::object *tool_component_obj,
+ json::object *message_obj,
+ json::object *rule_obj);
+ const char *
+ lookup_plain_text_within_result_message (json::object *tool_component_obj,
+ json::object *message_obj,
+ json::object *rule_obj);
+
+ location_t handle_location_object (json::object *location_obj,
+ json::object **out_logical_location_obj);
+ location_t handle_physical_location_object (json::object *phys_loc_obj);
+ const char *handle_artifact_location_object (json::object *artifact_loc_obj);
+ void handle_region_object (json::object *region_obj,
+ expanded_location *start_exp_loc,
+ expanded_location *end_exp_loc);
+
+ tree get_or_create_fndecl (const char *func_str);
+ tree get_or_create_fndecl (json::object *logical_location_obj);
+
+ void fatal_error_with_ref (json::value *jv, const spec_ref &ref,
+ const char *gmsgid, ...) ATTRIBUTE_GCC_DIAG(4,5)
+ {
+ location_t loc = m_json_loc_map.get_range_for_value (jv);
+
+ auto_diagnostic_group d;
+ va_list ap;
+ va_start (ap, gmsgid);
+ rich_location richloc (line_table, loc);
+ diagnostic_metadata metadata;
+ metadata.add_rule (ref);
+ emit_diagnostic_valist (DK_ERROR, &richloc, &metadata, 0, gmsgid, &ap);
+ va_end (ap);
+ exit (1);
+ // FIXME: ::fatal_error, but make it usable from DejaGnu
+ }
+
+private:
+ deferred_locations m_deferred_locs;
+
+ /* Map from physicalLocation object to pending_location_range ptr.
+ Populated in pass 0; used in pass 1. */
+ hash_map<json::object *, pending_location_range *> m_phys_loc_map;
+ // TODO: delete this in dtor
+
+ int m_pass;
+
+ hash_map<tree, tree> m_map_id_to_fndecl;
+
+ json::object *m_driver_obj;
+ json::array *m_artifacts_arr;
+ json::object *m_current_logical_loc_obj;
+};
+
+/* class current_driver_version_info : public client_version_info. */
+
+const char *
+current_driver_version_info::get_tool_name () const
+{
+ gcc_assert (m_replayer->m_driver_obj);
+
+ property_spec_ref name_prop ("toolComponent", "name", "3.19.8");
+ return m_replayer->get_optional_string_property
+ (m_replayer->m_driver_obj, name_prop);
+}
+
+char *
+current_driver_version_info::maybe_make_full_name () const
+{
+ gcc_assert (m_replayer->m_driver_obj);
+
+ property_spec_ref full_name_prop ("toolComponent", "fullName", "3.19.9");
+ if (const char *full_name = m_replayer->get_optional_string_property
+ (m_replayer->m_driver_obj, full_name_prop))
+ return xstrdup (full_name);
+ return NULL;
+}
+
+const char *
+current_driver_version_info::get_version_string () const
+{
+ gcc_assert (m_replayer->m_driver_obj);
+
+ property_spec_ref version_prop ("toolComponent", "version", "3.19.13");
+ return m_replayer->get_optional_string_property (m_replayer->m_driver_obj,
+ version_prop);
+}
+
+char *
+current_driver_version_info::maybe_make_version_url () const
+{
+ gcc_assert (m_replayer->m_driver_obj);
+
+ property_spec_ref info_uri_prop ("toolComponent", "informationUri",
+ "3.19.17");
+ if (const char *version_url = m_replayer->get_optional_string_property
+ (m_replayer->m_driver_obj, info_uri_prop))
+ return xstrdup (version_url);
+ return NULL;
+}
+
+void
+current_driver_version_info::for_each_plugin (plugin_visitor &visitor) const
+{
+ // TODO
+}
+
+/* class current_sarif_logical_location : public logical_location. */
+
+const char *
+current_sarif_logical_location::get_short_name () const
+{
+ gcc_assert (m_replayer->m_current_logical_loc_obj);
+ property_spec_ref name_prop ("logicalLocation", "name", "3.33.4");
+ return m_replayer->get_optional_string_property
+ (m_replayer->m_current_logical_loc_obj, name_prop);
+}
+
+const char *
+current_sarif_logical_location::get_name_with_scope () const
+{
+ gcc_assert (m_replayer->m_current_logical_loc_obj);
+ property_spec_ref fqname_prop ("logicalLocation", "fullyQualifiedName",
+ "3.33.5");
+ return m_replayer->get_optional_string_property
+ (m_replayer->m_current_logical_loc_obj, fqname_prop);
+}
+
+const char *
+current_sarif_logical_location::get_internal_name () const
+{
+ gcc_assert (m_replayer->m_current_logical_loc_obj);
+ property_spec_ref decorated_name_prop ("logicalLocation", "decoratedName",
+ "3.33.6");
+ return m_replayer->get_optional_string_property
+ (m_replayer->m_current_logical_loc_obj, decorated_name_prop);
+}
+
+enum logical_location_kind
+current_sarif_logical_location::get_kind () const
+{
+ gcc_assert (m_replayer->m_current_logical_loc_obj);
+ property_spec_ref kind_prop ("logicalLocation", "kind", "3.33.7");
+ if (const char *kind = m_replayer->get_optional_string_property
+ (m_replayer->m_current_logical_loc_obj, kind_prop))
+ {
+ if (!strcmp (kind, "function"))
+ return LOGICAL_LOCATION_KIND_FUNCTION;
+ if (!strcmp (kind, "member"))
+ return LOGICAL_LOCATION_KIND_MEMBER;
+ if (!strcmp (kind, "module"))
+ return LOGICAL_LOCATION_KIND_MODULE;
+ if (!strcmp (kind, "namespace"))
+ return LOGICAL_LOCATION_KIND_NAMESPACE;
+ if (!strcmp (kind, "type"))
+ return LOGICAL_LOCATION_KIND_TYPE;
+ if (!strcmp (kind, "returnType"))
+ return LOGICAL_LOCATION_KIND_RETURN_TYPE;
+ if (!strcmp (kind, "parameter"))
+ return LOGICAL_LOCATION_KIND_PARAMETER;
+ if (!strcmp (kind, "variable"))
+ return LOGICAL_LOCATION_KIND_VARIABLE;
+ /* TODO: maybe consolidate this with maybe_get_sarif_kind
+ in the producer code. */
+ }
+
+ return LOGICAL_LOCATION_KIND_UNKNOWN;
+}
+
+/* class sarif_diagnostic_client_data_hooks. */
+
+sarif_diagnostic_client_data_hooks::~sarif_diagnostic_client_data_hooks ()
+{
+ delete m_replayer;
+ delete m_toplevel_jv;
+}
+
+/* We only have a client_version_info * if m_replayer has found
+ a driver object. */
+
+const client_version_info *
+sarif_diagnostic_client_data_hooks::get_any_version_info () const
+{
+ if (!m_replayer->m_driver_obj)
+ return NULL;
+ return &m_driver_version_info;
+}
+
+/* We only have a current logical_location if m_replayer has one. */
+
+const logical_location *
+sarif_diagnostic_client_data_hooks::get_current_logical_location () const
+{
+ if (!m_replayer->m_current_logical_loc_obj)
+ return NULL;
+ return &m_current_logical_location;
+}
+
+const char *
+sarif_diagnostic_client_data_hooks::
+maybe_get_sarif_source_language (const char *filename) const
+{
+ if (!m_replayer->m_artifacts_arr)
+ return NULL;
+
+ for (auto iter : *m_replayer->m_artifacts_arr)
+ {
+ property_spec_ref artifacts_prop ("run", "artifacts", "3.14.15");
+ json::object *artifact_obj
+ = m_replayer->require_object_for_element (iter, artifacts_prop);
+
+ property_spec_ref location_prop ("artifact", "location", "3.24.2");
+ if (json::object *location_obj
+ = m_replayer->get_optional_object_property (artifact_obj,
+ location_prop))
+ {
+ property_spec_ref uri_prop ("artifactLocation", "uri", "3.4.3");
+ if (const char *uri
+ = m_replayer->get_optional_string_property (location_obj,
+ uri_prop))
+ if (!strcmp (uri, filename))
+ {
+ property_spec_ref source_lang_prop
+ ("artifact", "sourceLanguage", "3.24.10");
+ if (const char *source_language
+ = m_replayer->get_optional_string_property (artifact_obj,
+ source_lang_prop))
+ return source_language;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* sarif_replayer's ctor. */
+
+sarif_replayer::sarif_replayer (const char *filename)
+: json_reader (filename),
+ m_pass (0),
+ m_driver_obj (NULL),
+ m_artifacts_arr (NULL),
+ m_current_logical_loc_obj (NULL)
+{
+}
+
+/* sarif_replayer's dtor. */
+
+sarif_replayer::~sarif_replayer ()
+{
+ for (auto iter : m_phys_loc_map)
+ delete iter.second;
+}
+
+/* Perform one pass of replay of the output file.
+ Pass 0 captures the source locations of interest, so that we can generate
+ line_maps.
+ Pass 1 uses the line_maps. */
+
+void
+sarif_replayer::emit_sarif_as_diagnostics (json::value *jv, int pass)
+{
+ m_pass = pass;
+
+ /* We expect a sarifLog object as the top-level value
+ (SARIF v2.1.0 section 3.13). */
+ json::object *toplev_obj = dyn_cast <json::object *> (jv);
+ if (!toplev_obj)
+ fatal_error_with_ref (jv, spec_ref ("3.1"),
+ "expected a sarifLog object as the top-level value");
+
+ /* sarifLog objects SHALL have a property named "version"
+ (SARIF v2.1.0 section 3.13.2) with a string value. */
+ get_required_string_property (toplev_obj,
+ property_spec_ref ("sarifLog", "version",
+ "3.13.2"));
+
+ /* sarifLog.runs must be null or be an array. */
+ property_spec_ref prop_runs ("sarifLog", "runs", "3.13.4");
+ json::value *runs = get_required_property (toplev_obj, prop_runs);
+ switch (runs->get_kind ())
+ {
+ default:
+ fatal_error_with_ref (runs, prop_runs,
+ "expected sarifLog.runs to be"
+ " %<null%> or an array");
+ break;
+ case json::JSON_NULL:
+ /* Nothing to do. */
+ return;
+ case json::JSON_ARRAY:
+ {
+ json::array *runs_arr = as_a <json::array *> (runs);
+ for (auto element : *runs_arr)
+ {
+ json::object *run_obj
+ = require_object_for_element (element, prop_runs);
+ handle_run_obj (run_obj);
+ }
+ }
+ break;
+ }
+
+ if (m_pass == 0)
+ m_deferred_locs.generate_location_t_values ();
+}
+
+/* Process a run object (SARIF v2.1.0 section 3.14). */
+
+void
+sarif_replayer::handle_run_obj (json::object *run_obj)
+{
+ json::object *tool_obj
+ = get_required_object_property (run_obj,
+ property_spec_ref ("run", "tool",
+ "3.14.6"));
+
+ m_driver_obj
+ = get_required_object_property (tool_obj,
+ property_spec_ref ("tool", "driver",
+ "3.18.2"));
+
+ m_artifacts_arr
+ = get_optional_array_property (run_obj,
+ property_spec_ref ("run", "artifacts",
+ "3.14.15"));
+
+ /* If present, run.results must be null or be an array. */
+ property_spec_ref prop_results ("run", "results", "3.14.23");
+ json::value *results = run_obj->get ("results");
+ if (!results)
+ return;
+ switch (results->get_kind ())
+ {
+ default:
+ fatal_error_with_ref (results, prop_results,
+ "expected run.results to be"
+ " %<null%> or an array");
+ break;
+ case json::JSON_NULL:
+ /* Nothing to do. */
+ return;
+ case json::JSON_ARRAY:
+ {
+ json::array *results_arr = as_a <json::array *> (results);
+ for (auto element : *results_arr)
+ {
+ json::object *result_obj
+ = require_object_for_element (element, prop_results);
+ handle_result_obj (result_obj, tool_obj);
+ }
+ }
+ break;
+ }
+}
+
+/* Convert a SARIF result.level (3.27.10) to a GCC diagnostic kind. */
+
+static diagnostic_t
+get_diagnostic_kind_from_result_level (const char *level)
+{
+ if (!strcmp (level, "warning"))
+ return DK_WARNING;
+ if (!strcmp (level, "error"))
+ return DK_ERROR;
+ if (!strcmp (level, "note"))
+ return DK_NOTE;
+ return DK_UNSPECIFIED; // FIXME: what if this happens (e.g. with "none")
+}
+
+/* Concrete subclass of diagnostic_metadata::rule for a specific SARIF
+ rule object. */
+
+class sarif_rule : public diagnostic_metadata::rule
+{
+public:
+ sarif_rule (sarif_replayer *replayer, const char *rule_id,
+ json::object *rule_obj)
+ : m_replayer (replayer),
+ m_rule_id (rule_id),
+ m_rule_obj (rule_obj)
+ {}
+
+ char *make_description () const final override
+ {
+ if (m_rule_id)
+ return xstrdup (m_rule_id);
+ else
+ return NULL;
+ }
+
+ char *make_url () const final override
+ {
+ if (m_rule_obj)
+ {
+ property_spec_ref prop_help_uri
+ ("reportingDescriptor", "helpUri", "3.49.12");
+ if (const char *help_uri
+ = m_replayer->get_optional_string_property (m_rule_obj,
+ prop_help_uri))
+ return xstrdup (help_uri);
+ }
+ return NULL;
+ }
+
+private:
+ sarif_replayer *m_replayer;
+ const char *m_rule_id;
+ json::object *m_rule_obj;
+};
+
+/* If ITER_SRC starts with a placeholder as per §3.11.5, advance ITER_SRC
+ to immediately beyond the placeholder, write to *OUT_ARG_IDX, and
+ return true.
+
+ Otherwise, leave ITER_SRC untouched and return false. */
+
+static bool
+maybe_consume_placeholder (const char *&iter_src, unsigned *out_arg_idx)
+{
+ if (*iter_src != '{')
+ return false;
+ const char *first_digit = iter_src + 1;
+ const char *iter_digit = first_digit;
+ while (char ch = *iter_digit)
+ switch (ch)
+ {
+ default:
+ return false;
+
+ case '}':
+ if (iter_digit == first_digit)
+ {
+ /* No digits, we simply have "{}" which is not a placeholder
+ (and malformed: the braces should have been escaped). */
+ return false;
+ }
+ *out_arg_idx = atoi (first_digit);
+ iter_src = iter_digit + 1;
+ return true;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ // FIXME: what about multiple leading zeroes?
+ iter_digit++;
+ continue;
+ }
+ return false; // TODO
+}
+
+/* Lookup the plain text string within a result.message (§3.27.11),
+ and substitute for any placeholders (§3.11.5).
+ TODO: embedded links?
+
+ MESSAGE_OBJ is "theMessage"
+ RULE_OBJ is "theRule". */
+
+char *
+sarif_replayer::
+make_plain_text_within_result_message (json::object *tool_component_obj,
+ json::object *message_obj,
+ json::object *rule_obj)
+{
+ const char *original_text
+ = lookup_plain_text_within_result_message (tool_component_obj,
+ message_obj,
+ rule_obj);
+ if (!original_text)
+ return NULL;
+
+ /* Look up any arguments for substituting into placeholders. */
+ property_spec_ref arguments_prop ("message", "arguments", "3.11.11");
+ json::array *arguments
+ = get_optional_array_property (message_obj, arguments_prop);
+
+ /* Duplicate original_text, substituting any placeholders. */
+ pretty_printer pp;
+
+ const char *iter_src = original_text;
+ while (char ch = *iter_src)
+ {
+ unsigned arg_idx;
+ if (maybe_consume_placeholder (iter_src, &arg_idx))
+ {
+ if (!arguments)
+ fatal_error_with_ref (message_obj, arguments_prop,
+ "message string contains placeholder %<{%i}%>"
+ " but message object has no %qs property",
+ (int)arg_idx,
+ arguments_prop.get_property_name ());
+ if (arg_idx >= arguments->length ())
+ fatal_error_with_ref (message_obj, arguments_prop,
+ "not enough strings in %qs array for"
+ " placeholder %<{%i}%>",
+ arguments_prop.get_property_name (),
+ (int)arg_idx);
+ const char *replacement_str
+ = require_string (arguments->get (arg_idx), arguments_prop);
+ pp_string (&pp, replacement_str);
+ }
+ else if (ch == '{' || ch == '}')
+ {
+ /* '{' and '}' are escaped by repeating them. */
+ if (iter_src[1] == ch)
+ {
+ pp_character (&pp, ch);
+ iter_src += 2;
+ }
+ else
+ fatal_error_with_ref (message_obj, arguments_prop,
+ "unescaped '%c' within message string", ch);
+ }
+ else
+ pp_character (&pp, *(iter_src++));
+ }
+
+ return xstrdup (pp_formatted_text (&pp));
+}
+
+ /* IF theMessage.text is present and the desired language is theRun.language THEN
+ Use the text or markdown property of theMessage as appropriate.
+ IF the string has not yet been found THEN
+ IF theMessage occurs as the value of result.message (§3.27.11) THEN
+ LET theRule be the reportingDescriptor object (§3.49), an element of theComponent.rules (§3.19.23), which defines the rule that was violated by this result.
+ IF theRule exists AND theRule.messageStrings (§3.49.11) is present AND contains a property whose name equals theMessage.id THEN
+ LET theMFMS be the multiformatMessageString object (§3.12) that is the value of that property.
+ Use the text or markdown property of theMFMS as appropriate.
+ ELSE IF theMessage occurs as the value of notification.message (§3.58.5) THEN
+ LET theDescriptor be the reportingDescriptor object (§3.49), an element of theComponent.notifications (§3.19.23), which describes this notification.
+ IF theDescriptor exists AND theDescriptor.messageStrings is present AND contains a property whose name equals theMessage.id THEN
+ LET theMFMS be the multiformatMessageString object that is the value of that property.
+ Use the text or markdown property of theMFMS as appropriate.
+ IF the string has not yet been found THEN
+ IF theComponent.globalMessageStrings (§3.19.22) is present AND contains a property whose name equals theMessage.id THEN
+ LET theMFMS be the multiformatMessageString object that is the value of that property.
+ Use the text or markdown property of theMFMS as appropriate.
+ IF the string has not yet been found THEN
+ The lookup procedure fails (which means the SARIF log file is invalid).
+ */
+
+/* Implement the message string lookup algorithm from
+ SARIF v2.1.0 section 3.11.7, for the case where theMessage
+ is the value of result.message (§3.27.11).
+
+ MESSAGE_OBJ is "theMessage"
+ RULE_OBJ is "theRule". */
+
+const char *
+sarif_replayer::
+lookup_plain_text_within_result_message (json::object *tool_component_obj,
+ json::object *message_obj,
+ json::object *rule_obj)
+{
+ gcc_assert (message_obj);
+ // rule_obj can be NULL
+
+ /* IF theMessage.text is present and the desired language is theRun.language THEN
+ Use the text or markdown property of theMessage as appropriate. */
+ if (const char *text
+ = get_optional_string_property (message_obj,
+ property_spec_ref ("message", "text",
+ "3.11.8")))
+ // TODO: check language
+ return text;
+
+ const char *message_id
+ = get_optional_string_property (message_obj,
+ property_spec_ref ("message", "id",
+ "3.11.10"));
+
+ /* LET theRule be the reportingDescriptor object (§3.49), an element of theComponent.rules (§3.19.23), which defines the rule that was violated by this result.
+ IF theRule exists AND theRule.messageStrings (§3.49.11) is present AND contains a property whose name equals theMessage.id THEN
+ LET theMFMS be the multiformatMessageString object (§3.12) that is the value of that property.
+ Use the text or markdown property of theMFMS as appropriate.
+ */
+ if (rule_obj && message_id)
+ {
+ property_spec_ref message_strings ("reportingDescriptor",
+ "messageStrings",
+ "3.49.11");
+ if (json::object *message_strings_obj
+ = get_optional_object_property (rule_obj, message_strings))
+ {
+ if (json::value *mfms = message_strings_obj->get (message_id))
+ {
+ json::object *mfms_obj = require_object (mfms, message_strings);
+
+ // 3.12 multiformatMessageString object
+ const char *text = get_required_string_property
+ (mfms_obj,
+ property_spec_ref ("multiformatMessageString", "text",
+ "3.12.3"));
+ return text;
+ }
+ }
+ }
+
+ // TODO:
+ /* IF the string has not yet been found THEN
+ IF theComponent.globalMessageStrings (§3.19.22) is present AND contains a property whose name equals theMessage.id THEN
+ LET theMFMS be the multiformatMessageString object that is the value of that property.
+ Use the text or markdown property of theMFMS as appropriate.
+ */
+
+ /* Failure. */
+ fatal_error_with_ref (message_obj, spec_ref ("3.11.7"),
+ "could not find string for message object");
+ return NULL;
+}
+
+/* FIXME. */
+// 3.52.3 reportingDescriptor lookup
+// "For an example of the interaction between ruleId and rule.id, see §3.52.4."
+
+json::object *
+sarif_replayer::lookup_rule_by_id_in_tool (const char *rule_id,
+ json::object *tool_obj)
+{
+ if (!rule_id)
+ return NULL;
+ if (!tool_obj)
+ return NULL;
+
+ json::object *driver_obj
+ = get_required_object_property (tool_obj,
+ property_spec_ref ("tool", "driver",
+ "3.18.2"));
+
+ if (json::object *rule_obj
+ = lookup_rule_by_id_in_component (rule_id, driver_obj))
+ return rule_obj;
+
+ // TODO: also handle extensions
+
+ return NULL;
+}
+
+/* FIXME. */
+
+json::object *
+sarif_replayer::lookup_rule_by_id_in_component (const char *rule_id,
+ json::object *tool_component_obj)
+{
+ property_spec_ref rules ("toolComponent", "rules", "3.18.2");
+
+ json::array *rules_arr
+ = get_optional_array_property (tool_component_obj, rules);
+ if (!rules_arr)
+ return NULL;
+
+ for (auto element : *rules_arr)
+ {
+ json::object *reporting_desc_obj
+ = require_object_for_element (element, rules);
+
+ /* reportingDescriptor objects (§3.49). */
+ property_spec_ref id ("reportingDescriptor", "id", "3.49.3");
+ const char *desc_id
+ = get_required_string_property (reporting_desc_obj, id);
+ if (!strcmp (rule_id, desc_id))
+ return reporting_desc_obj;
+ }
+
+ return NULL;
+}
+
+/* Ensure that we have a placeholder fndecl named FUNC_STR.
+ All such placeholder functions merely have signature
+ void FUNC_STR (void); */
+
+tree
+sarif_replayer::get_or_create_fndecl (const char *func_str)
+{
+ tree id = get_identifier (func_str);
+ if (tree *slot = m_map_id_to_fndecl.get (id))
+ return *slot;
+ tree fntype_void_void
+ = build_function_type_array (void_type_node, 0, NULL);
+ tree fn_type = fntype_void_void;
+ tree fndecl = build_fn_decl (func_str, fn_type);
+ m_map_id_to_fndecl.put (id, fndecl);
+ return fndecl;
+}
+
+/* Attempt to get a placeholder fndecl for the given SARIF logicalLocation
+ object (3.33).
+
+ All such placeholder functions merely have signature
+ void FUNC_STR (void); */
+
+tree
+sarif_replayer::get_or_create_fndecl (json::object *logical_location_obj)
+{
+ /* First try "name". */
+ property_spec_ref prop_name ("logicalLocation", "name", "3.33.4");
+ if (const char *name
+ = get_optional_string_property (logical_location_obj, prop_name))
+ return get_or_create_fndecl (name);
+
+ /* Failing that, try "fullyQualifiedName". */
+ property_spec_ref
+ prop_fqname ("logicalLocation", "fullyQualifiedName", "3.33.5");
+ if (const char *fqname
+ = get_optional_string_property (logical_location_obj, prop_fqname))
+ return get_or_create_fndecl (fqname);
+
+ /* Failure. */
+ return NULL;
+}
+
+/* Concrete subclass of diagnostic_event for use when replaying diagnostics
+ from a SARIF file.
+
+ Corresponds to a threadFlowLocation object (§3.38). */
+
+class sarif_replay_diagnostic_event : public diagnostic_event
+{
+public:
+ sarif_replay_diagnostic_event (sarif_replayer *replayer,
+ json::object *tflow_loc_obj)
+ : m_replayer (replayer),
+ m_tflow_loc_obj (tflow_loc_obj),
+ m_logical_location_obj (NULL),
+ m_loc (UNKNOWN_LOCATION),
+ m_fndecl (NULL),
+ m_stack_depth (0),
+ m_message (NULL),
+ m_meaning ()
+ {
+ property_spec_ref location_prop ("threadFlowLocation", "location",
+ "3.38.3");
+ if (json::object *location_obj
+ = replayer->get_optional_object_property (tflow_loc_obj, location_prop))
+ {
+ /* location object (§3.28). */
+ m_loc = replayer->handle_location_object (location_obj,
+ &m_logical_location_obj);
+ if (m_logical_location_obj)
+ m_fndecl = replayer->get_or_create_fndecl (m_logical_location_obj);
+
+ /* Get any message from here. */
+ property_spec_ref location_message ("location", "message", "3.28.5");
+ if (json::object *message_obj
+ = replayer->get_optional_object_property (location_obj,
+ location_message))
+ m_message = replayer->make_plain_text_within_result_message
+ (NULL,
+ message_obj,
+ NULL/* FIXME. */);
+ }
+
+ // 3.38.8 kinds property
+ // TODO: populate m_meaning
+
+ /* nestingLevel property (§3.38.10). */
+ property_spec_ref nesting_level ("threadFlowLocation", "nestingLevel",
+ "3.38.10");
+ replayer->get_optional_int_property
+ (&m_stack_depth, tflow_loc_obj, nesting_level);
+ if (m_stack_depth < 0)
+ replayer->fatal_error_with_ref (tflow_loc_obj, nesting_level,
+ "expected a non-negative integer");
+ }
+ ~sarif_replay_diagnostic_event ()
+ {
+ free (m_message);
+ }
+
+ location_t get_location () const final override { return m_loc; }
+ tree get_fndecl () const final override { return m_fndecl; }
+ int get_stack_depth () const final override { return m_stack_depth; }
+ label_text get_desc (bool) const final override
+ {
+ return label_text::borrow (m_message ? m_message : "");
+ }
+ const logical_location *get_logical_location () const final override
+ {
+ return NULL; // TODO
+ }
+ meaning get_meaning () const final override
+ {
+ return m_meaning;
+ }
+
+private:
+ sarif_replayer *m_replayer;
+ json::object *m_tflow_loc_obj;
+ json::object *m_logical_location_obj;
+ location_t m_loc;
+ tree m_fndecl;
+ int m_stack_depth;
+ char *m_message; // has been i18n-ed and formatted, or is NULL
+ meaning m_meaning;
+};
+
+/* Concrete subclass of diagnostic_path for use when replaying diagnostics
+ from a SARIF file. */
+
+class sarif_replay_diagnostic_path : public diagnostic_path
+{
+public:
+ unsigned num_events () const final override { return m_events.length (); }
+ const diagnostic_event & get_event (int idx) const final override
+ {
+ return *m_events[idx];
+ }
+
+ void add_event (sarif_replay_diagnostic_event *event)
+ {
+ m_events.safe_push (event);
+ }
+
+private:
+ auto_delete_vec<sarif_replay_diagnostic_event> m_events;
+};
+
+/* Make a new diagnostic_path instance for THREAD_FLOW_OBJ, a
+ SARIF threadFlow object (section 3.37). */
+
+diagnostic_path *
+sarif_replayer::make_sarif_diagnostic_path (json::object *thread_flow_obj)
+{
+ property_spec_ref locations ("threadFlow", "locations", "3.37.6");
+ json::array *locations_arr
+ = get_required_array_property (thread_flow_obj, locations);
+ sarif_replay_diagnostic_path *path = new sarif_replay_diagnostic_path ();
+ for (auto location : *locations_arr)
+ {
+ /* threadFlowLocation object (§3.38). */
+ json::object *tflow_loc_obj
+ = require_object_for_element (location, locations);
+
+ sarif_replay_diagnostic_event *event
+ = new sarif_replay_diagnostic_event (this, tflow_loc_obj);
+ path->add_event (event);
+ }
+ return path;
+}
+
+/* TODO. */
+// location object (§3.28)
+
+location_t
+sarif_replayer::handle_location_object (json::object *location_obj,
+ json::object **out_logical_location_obj)
+{
+ location_t loc = UNKNOWN_LOCATION;
+
+ // §3.28.3 physicalLocation property
+ {
+ property_spec_ref physical_location_prop ("location", "physicalLocation",
+ "3.28.3");
+ if (json::object *phys_loc_obj
+ = get_optional_object_property (location_obj, physical_location_prop))
+ loc = handle_physical_location_object (phys_loc_obj);
+ }
+
+ // §3.28.4 logicalLocations property
+ {
+ property_spec_ref logical_locations_prop ("location", "logicalLocations",
+ "3.28.4");
+ if (json::array *logical_loc_arr
+ = get_optional_array_property (location_obj, logical_locations_prop))
+ if (logical_loc_arr->length () > 0)
+ {
+ /* Only look at the first, if there's more than one. */
+ *out_logical_location_obj
+ = require_object_for_element (logical_loc_arr->get (0),
+ logical_locations_prop);
+ }
+ }
+
+ return loc;
+}
+
+/* TODO. */
+// physicalLocation object (§3.29)
+
+location_t
+sarif_replayer::handle_physical_location_object (json::object *phys_loc_obj)
+{
+ expanded_location start_exp_loc = {NULL, 0, 0, NULL, false};
+ expanded_location end_exp_loc = {NULL, 0, 0, NULL, false};
+
+ const char *filename = NULL;
+ //3.29.3 artifactLocation property
+ property_spec_ref artifact_location_prop ("physicalLocation", "artifactLocation",
+ "3.29.3");
+ if (json::object *artifact_loc_obj
+ = get_optional_object_property (phys_loc_obj, artifact_location_prop))
+ if (const char *filename
+ = handle_artifact_location_object (artifact_loc_obj))
+ {
+ start_exp_loc.file = filename;
+ end_exp_loc.file = filename;
+ }
+
+ //3.29.4 region property
+ property_spec_ref region_prop ("physicalLocation", "region", "3.29.4");
+ if (json::object *region_obj
+ = get_optional_object_property (phys_loc_obj, region_prop))
+ handle_region_object (region_obj, &start_exp_loc, &end_exp_loc);
+
+ // FIXME: what about ranges???
+
+ // TODO:
+ //3.29.5 contextRegion property
+ //3.29.6 address property
+
+ if (m_pass == 0)
+ {
+ pending_location_range *pending_range = new pending_location_range ();
+ m_deferred_locs.add_location (start_exp_loc, &pending_range->m_start);
+ m_deferred_locs.add_location (end_exp_loc, &pending_range->m_end);
+ m_phys_loc_map.put (phys_loc_obj, pending_range);
+ return UNKNOWN_LOCATION;
+ }
+ else
+ {
+ pending_location_range *pending_range
+ = *m_phys_loc_map.get (phys_loc_obj);
+ return make_location (pending_range->m_start,
+ pending_range->m_start,
+ pending_range->m_end);
+ }
+}
+
+// TODO
+// artifactLocation object (§3.4)
+
+const char *
+sarif_replayer::handle_artifact_location_object (json::object *artifact_loc_obj)
+{
+ // TODO
+ // 3.4.3 uri property
+ property_spec_ref uri_prop ("artifactLocation", "uri", "3.4.3");
+ const char *uri = get_optional_string_property (artifact_loc_obj, uri_prop);
+ return uri;
+
+ // TODO
+ // 3.4.4 uriBaseId property
+ // 3.4.5 index property
+}
+
+// TODO
+// region object (§3.30)
+
+void
+sarif_replayer::handle_region_object (json::object *region_obj,
+ expanded_location *start_exp_loc,
+ expanded_location *end_exp_loc)
+{
+#if 0
+ if (pass == 0)
+ {
+ sarif::region_object *obj = new region_object (region_obj);
+ }
+#endif
+ // TODO
+ // TODO: 3.30.5 startLine property
+ property_spec_ref start_line_prop ("region", "startLine", "3.30.5");
+ int start_line;
+ if (get_optional_int_property (&start_line, region_obj, start_line_prop))
+ {
+ /* Text region defined by line/column properties. */
+ start_exp_loc->line = start_line;
+ end_exp_loc->column = 1;
+
+ int start_column;
+ property_spec_ref start_column_prop ("region", "startColumn", "3.30.6");
+ if (get_optional_int_property (&start_column, region_obj,
+ start_column_prop))
+ start_exp_loc->column = start_column;
+ else
+ start_exp_loc->column = 1;
+
+ int end_line;
+ property_spec_ref end_line_prop ("region", "endLine", "3.30.7");
+ if (get_optional_int_property (&end_line, region_obj, end_line_prop))
+ end_exp_loc->line = end_line;
+ else
+ end_exp_loc->line = start_line;
+
+ int end_column;
+ property_spec_ref end_column_prop ("region", "endColumn", "3.30.8");
+ if (get_optional_int_property (&end_column, region_obj,
+ end_column_prop))
+ {
+ /* SARIF's endColumn is 1 beyond the final column in the region,
+ whereas GCC's end columns are inclusive. */
+ end_exp_loc->column = end_column - 1;
+ }
+ else
+ {
+ // missing "endColumn" means the whole of the rest of the row
+ // TODO
+ }
+ }
+}
+
+/* Process a result object (SARIF v2.1.0 section 3.27). */
+
+void
+sarif_replayer::handle_result_obj (json::object *result_obj,
+ json::object *tool_obj)
+{
+ const char *rule_id
+ = get_optional_string_property (result_obj,
+ property_spec_ref ("result", "ruleId",
+ "3.27.5"));
+ json::object *rule_obj = lookup_rule_by_id_in_tool (rule_id, tool_obj);
+
+ // 3.27.6 ruleIndex property
+
+ // TODO: 3.27.8 taxa property
+ if (json::array *taxa_arr
+ = get_optional_array_property (result_obj,
+ property_spec_ref ("result", "taxa",
+ "3.27.8")))
+ {
+ // TODO:
+ }
+
+ diagnostic_t diag_kind = DK_WARNING;
+ if (const char *level
+ = get_optional_string_property (result_obj,
+ property_spec_ref ("result", "level",
+ "3.27.10")))
+ diag_kind = get_diagnostic_kind_from_result_level (level);
+
+ // 3.27.11 message property
+ char *text = NULL;
+ json::object *message_obj
+ = get_optional_object_property (result_obj,
+ property_spec_ref ("result", "message",
+ "3.27.11"));
+ if (message_obj)
+ text = make_plain_text_within_result_message (NULL, // FIXME: tool_component_obj,
+ message_obj,
+ rule_obj);
+
+ // 3.27.12 locations property
+ json::object *logical_location_obj = NULL;
+ tree fndecl = NULL;
+ location_t loc = UNKNOWN_LOCATION;
+ property_spec_ref locations_prop ("result", "locations", "3.27.12");
+ json::array *locations_arr
+ = get_required_array_property (result_obj, locations_prop);
+ if (locations_arr->length () > 0)
+ {
+ /* Only look at the first, if there's more than one. */
+ // location objects (§3.28)
+ json::object *location_obj
+ = require_object_for_element (locations_arr->get (0), locations_prop);
+ loc = handle_location_object (location_obj, &logical_location_obj);
+ if (logical_location_obj)
+ fndecl = get_or_create_fndecl (logical_location_obj);
+ }
+
+ // 3.27.18 codeFlows property
+ diagnostic_path *path = NULL;
+ property_spec_ref code_flows ("result", "codeFlows", "3.27.18");
+ if (json::array *code_flows_arr
+ = get_optional_array_property (result_obj, code_flows))
+ {
+ if (code_flows_arr->length () == 1)
+ {
+ json::object *code_flow_obj
+ = require_object_for_element (code_flows_arr->get (0), code_flows);
+
+ property_spec_ref thread_flows ("result", "threadFlows", "3.36.3");
+ if (json::array *thread_flows_arr
+ = get_optional_array_property (code_flow_obj, thread_flows))
+ {
+ if (thread_flows_arr->length () == 1)
+ {
+ json::object *thread_flow_obj
+ = require_object_for_element (thread_flows_arr->get (0),
+ thread_flows);
+ path = make_sarif_diagnostic_path (thread_flow_obj);
+ }
+ }
+ }
+ }
+
+ // TODO: 3.27.22 relatedLocations property
+
+ // TODO: 3.27.30 fixes property
+
+ // TODO: use logical_location_obj, if non-NULL
+ //sarif_logical_location logical_loc (logical_location_obj);
+ //if (logical_location_obj)
+ m_current_logical_loc_obj = logical_location_obj;
+ current_function_decl = fndecl;
+ rich_location rich_loc (line_table, loc);
+ rich_loc.set_path (path);
+ sarif_rule rule (this, rule_id, rule_obj);
+ diagnostic_metadata metadata;
+ metadata.add_rule (rule);
+ auto_diagnostic_group d;
+ if (m_pass == 1)
+ if (emit_diagnostic (diag_kind, &rich_loc, &metadata, 0, "%s",
+ text ? text : "FIXME"))
+ {
+ // TODO
+ }
+ free (text);
+ delete path;
+ current_function_decl = NULL;
+ m_current_logical_loc_obj = NULL;
+}
+
+/* Attempt to load a SARIF file from FILENAME and replay it.
+ Exit on any errors. */
+
+void
+replay_sarif (const char *filename)
+{
+ sarif_replayer *p = new sarif_replayer (filename);
+ sarif_diagnostic_client_data_hooks *hooks
+ = new sarif_diagnostic_client_data_hooks (p);
+ global_dc->m_client_data_hooks = hooks;
+
+ char *content = read_file (filename);
+ json::error *err = NULL;
+ json::value *jv = p->parse_utf8_string (content, flag_allow_comments, &err);
+ if (err)
+ {
+ p->fatal_error (err);
+ delete err;
+ }
+ free (content);
+
+ if (jv)
+ {
+ for (int pass = 0; pass < 2; pass++)
+ p->emit_sarif_as_diagnostics (jv, pass);
+ hooks->stash (jv);
+ }
+
+ // global_dc's client_data takes ownership of "p"
+ // TODO: should it take ownership of jv?
+ // TODO: or should we clone jv?
+}
diff --git a/gcc/sarif/sarif-replay.h b/gcc/sarif/sarif-replay.h
new file mode 100644
index 00000000000..fb2aa79dee2
--- /dev/null
+++ b/gcc/sarif/sarif-replay.h
@@ -0,0 +1,26 @@
+/* Re-emitting diagnostics saved in SARIF form.
+ Copyright (C) 2022 David Malcolm <dmalcolm@redhat.com>.
+ Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_SARIF_SARIF_REPLAY_H
+#define GCC_SARIF_SARIF_REPLAY_H
+
+extern void replay_sarif (const char *filename);
+
+#endif /* GCC_SARIF_SARIF_H */
diff --git a/gcc/testsuite/lib/sarif-dg.exp b/gcc/testsuite/lib/sarif-dg.exp
new file mode 100644
index 00000000000..c82d7a131a1
--- /dev/null
+++ b/gcc/testsuite/lib/sarif-dg.exp
@@ -0,0 +1,233 @@
+# Copyright (C) 2004-2022 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+load_lib gcc-dg.exp
+load_lib torture-options.exp
+
+#FIXME: copied from gfortran-dg.exp
+
+# Define sarif callbacks for dg.exp.
+
+proc sarif-dg-test { prog do_what extra_tool_flags } {
+ set result \
+ [gcc-dg-test-1 sarif_target_compile $prog $do_what $extra_tool_flags]
+
+ set comp_output [lindex $result 0]
+ set output_file [lindex $result 1]
+
+ # gcc's default is to print the caret and source code, but
+ # most test cases implicitly use the flag -fno-diagnostics-show-caret
+ # to disable caret (and source code) printing.
+ #
+ # However, a few test cases override this back to the default by
+ # explicily supplying "-fdiagnostics-show-caret", so that we can have
+ # test coverage for caret/source code printing.
+ #
+ # sarif error messages with caret-printing look like this:
+ # [name]:[locus]:
+ #
+ # some code
+ # 1
+ # Error: Some error at (1)
+ # or
+ # [name]:[locus]:
+ #
+ # some code
+ # 1
+ # [name]:[locus2]:
+ #
+ # some other code
+ # 2
+ # Error: Some error at (1) and (2)
+ # or
+ # [name]:[locus]:
+ #
+ # some code and some more code
+ # 1 2
+ # Error: Some error at (1) and (2)
+ #
+ # If this is such a test case, skip the rest of this function, so
+ # that the test case can explicitly verify the output that it expects.
+ if {[string first "-fdiagnostics-show-caret" $extra_tool_flags] >= 0} {
+ return [list $comp_output $output_file]
+ }
+
+ # Otherwise, caret-printing is disabled.
+ # sarif errors with caret-printing disabled look like this:
+ # [name]:[locus]: Error: Some error
+ # or
+ # [name]:[locus]: Error: (1)
+ # [name]:[locus2]: Error: Some error at (1) and (2)
+ #
+ # Where [locus] is either [line] or [line].[column] or
+ # [line].[column]-[column] .
+ #
+ # We collapse these to look like:
+ # [name]:[line]:[column]: Error: Some error at (1) and (2)
+ # or
+ # [name]:[line]:[column]: Error: Some error at (1) and (2)
+ # [name]:[line2]:[column]: Error: Some error at (1) and (2)
+ #
+ # Note that these regexps only make sense in the combinations used below.
+ # Note also that is imperative that we first deal with the form with
+ # two loci.
+ set locus_regexp "(\[^\n\]+:\[0-9\]+)\[\.:\](\[0-9\]+)(-\[0-9\]+)?:\n\n\[^\n\]+\n\[^\n\]+\n"
+ set diag_regexp "(\[^\n\]+)\n"
+
+ # We proceed in steps:
+
+ # 1. We add first a column number if none exists.
+ # (Some Fortran diagnostics have the locus after Warning|Error)
+ set colnum_regexp "(^|\n)(Warning: |Error: )?(\[^:\n\]+:\[0-9\]+):(\[ \n\])"
+ regsub -all $colnum_regexp $comp_output "\\1\\3:0:\\4\\2" comp_output
+ verbose "comput_output0:\n$comp_output"
+
+ # 2. We deal with the form with two different locus lines,
+ set two_loci "(^|\n)$locus_regexp$locus_regexp$diag_regexp"
+ regsub -all $two_loci $comp_output "\\1\\2:\\3: \\8\n\\5\:\\6: \\8\n" comp_output
+ verbose "comput_output1:\n$comp_output"
+
+ set locus_prefix "(\[^:\n\]+:\[0-9\]+:\[0-9\]+: )(Warning: |Error: )"
+ set two_loci2 "(^|\n)$locus_prefix\\(1\\)\n$locus_prefix$diag_regexp"
+ regsub -all $two_loci2 $comp_output "\\1\\2\\3\\6\n\\4\\5\\6\n" comp_output
+ verbose "comput_output2:\n$comp_output"
+
+ # 3. then with the form with only one locus line.
+ set single_locus "(^|\n)$locus_regexp$diag_regexp"
+ regsub -all $single_locus $comp_output "\\1\\2:\\3: \\5\n" comp_output
+ verbose "comput_output3:\n$comp_output"
+
+ # 4. Add a line number if none exists
+ regsub -all "(^|\n)(Warning: |Error: )" $comp_output "\\1:0:0: \\2" comp_output
+ verbose "comput_output4:\n$comp_output"
+ return [list $comp_output $output_file]
+}
+
+proc sarif-dg-prune { system text } {
+ return [gcc-dg-prune $system $text]
+}
+
+# Utility routines.
+
+# Modified dg-runtest that can cycle through a list of optimization options
+# as c-torture does.
+proc sarif-dg-runtest { testcases flags default-extra-flags } {
+ global runtests
+ global torture_with_loops
+
+ # Some callers set torture options themselves; don't override those.
+ set existing_torture_options [torture-options-exist]
+ if { $existing_torture_options == 0 } {
+ global DG_TORTURE_OPTIONS
+ torture-init
+ set-torture-options $DG_TORTURE_OPTIONS
+ }
+ dump-torture-options
+
+ foreach test $testcases {
+ # If we're only testing specific files and this isn't one of
+ # them, skip it.
+ if ![runtest_file_p $runtests $test] {
+ continue
+ }
+
+ # look if this is dg-do-run test, in which case
+ # we cycle through the option list, otherwise we don't
+ if [expr [search_for $test "dg-do run"]] {
+ set option_list $torture_with_loops
+ } else {
+ set option_list [list { -O } ]
+ }
+
+ set nshort [file tail [file dirname $test]]/[file tail $test]
+ list-module-names $test
+
+ foreach flags_t $option_list {
+ verbose "Testing $nshort, $flags $flags_t" 1
+ dg-test $test "$flags $flags_t" ${default-extra-flags}
+ cleanup-modules ""
+ }
+ }
+
+ if { $existing_torture_options == 0 } {
+ torture-finish
+ }
+}
+
+proc sarif-dg-debug-runtest { target_compile trivial opt_opts testcases } {
+ global srcdir subdir DEBUG_TORTURE_OPTIONS
+
+ if ![info exists DEBUG_TORTURE_OPTIONS] {
+ set DEBUG_TORTURE_OPTIONS ""
+ set type_list [list "-gstabs" "-gstabs+" "-gxcoff" "-gxcoff+" "-gdwarf-2" ]
+ foreach type $type_list {
+ set comp_output [$target_compile \
+ "$srcdir/$subdir/$trivial" "trivial.S" assembly \
+ "additional_flags=$type"]
+ if { [string match "exit status *" $comp_output] } {
+ continue
+ }
+ if { [string match \
+ "* target system does not support the * debug format*" \
+ $comp_output]
+ } {
+ continue
+ }
+ remove-build-file "trivial.S"
+ foreach level {1 "" 3} {
+ if { ($type == "-gdwarf-2") && ($level != "") } {
+ lappend DEBUG_TORTURE_OPTIONS [list "${type}" "-g${level}"]
+ foreach opt $opt_opts {
+ lappend DEBUG_TORTURE_OPTIONS \
+ [list "${type}" "-g${level}" "$opt" ]
+ }
+ } else {
+ lappend DEBUG_TORTURE_OPTIONS [list "${type}${level}"]
+ foreach opt $opt_opts {
+ lappend DEBUG_TORTURE_OPTIONS \
+ [list "${type}${level}" "$opt" ]
+ }
+ }
+ }
+ }
+ }
+
+ verbose -log "Using options $DEBUG_TORTURE_OPTIONS"
+
+ global runtests
+
+ foreach test $testcases {
+ # If we're only testing specific files and this isn't one of
+ # them, skip it.
+ if ![runtest_file_p $runtests $test] {
+ continue
+ }
+
+ set nshort [file tail [file dirname $test]]/[file tail $test]
+ list-module-names $test
+
+ foreach flags $DEBUG_TORTURE_OPTIONS {
+ set doit 1
+ # gcc-specific checking removed here
+
+ if { $doit } {
+ verbose -log "Testing $nshort, $flags" 1
+ dg-test $test $flags ""
+ cleanup-modules ""
+ }
+ }
+ }
+}
diff --git a/gcc/testsuite/lib/sarif.exp b/gcc/testsuite/lib/sarif.exp
new file mode 100644
index 00000000000..925d2787017
--- /dev/null
+++ b/gcc/testsuite/lib/sarif.exp
@@ -0,0 +1,36 @@
+# Copyright (C) 2003-2022 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# FIXME: copied from gfortran.exp
+
+# This file is just 'sed -e 's/77/fortran/g' \
+# -e 's/f2c/gfortran' g77.exp > gfortran.exp'
+#
+# with some minor modifications to make it work.
+
+#
+# sarif support library routines
+#
+load_lib prune.exp
+load_lib gcc-defs.exp
+load_lib timeout.exp
+load_lib target-libpath.exp
+load_lib target-supports.exp
+
+proc sarif_target_compile { source dest type options } {
+ set return_val [target_compile $source $dest $type $options]
+ return $return_val;
+}
diff --git a/gcc/testsuite/sarif/bad-eval-with-code-flow.py b/gcc/testsuite/sarif/bad-eval-with-code-flow.py
new file mode 100644
index 00000000000..e72d8de48a5
--- /dev/null
+++ b/gcc/testsuite/sarif/bad-eval-with-code-flow.py
@@ -0,0 +1,10 @@
+# Taken from https://github.com/microsoft/sarif-tutorials
+# samples/3-Beyond-basics/bad-eval-with-code-flow.py
+# which is licensed under MIT License.
+
+print("Hello, world!")
+expr = input("Expression> ")
+use_input(expr)
+
+def use_input(raw_input):
+ print(eval(raw_input))
diff --git a/gcc/testsuite/sarif/escaped-braces.sarif b/gcc/testsuite/sarif/escaped-braces.sarif
new file mode 100644
index 00000000000..8374a94b835
--- /dev/null
+++ b/gcc/testsuite/sarif/escaped-braces.sarif
@@ -0,0 +1,19 @@
+{
+ "version": "2.1.0",
+ "runs": [{
+ "tool": { "driver": { "name": "example" } },
+ "results": [
+ { "message": { "text" : "before open '{{' after open" },
+ "locations": []},
+ { "message": { "text" : "before close '}}' after close" },
+ "locations": []}
+ ]
+ }]
+}
+
+/* { dg-begin-multiline-output "" }
+sarif-replay: warning: before open '{' after open
+sarif-replay: warning: before close '}' after close
+ { dg-end-multiline-output "" } */
+
+// TODO: lose the "sarif-replay: " prefixes
diff --git a/gcc/testsuite/sarif/invalid-json-array-missing-comma.sarif b/gcc/testsuite/sarif/invalid-json-array-missing-comma.sarif
new file mode 100644
index 00000000000..0f32d38420e
--- /dev/null
+++ b/gcc/testsuite/sarif/invalid-json-array-missing-comma.sarif
@@ -0,0 +1,6 @@
+[ "foo", "bar" "baz"] // { dg-error "expected ',' or '\]'; got string" }
+
+{ dg-begin-multiline-output "" }
+ 1 | [ "foo", "bar" "baz"]
+ | ^~~~~
+{ dg-end-multiline-output "" }
diff --git a/gcc/testsuite/sarif/invalid-json-array-with-trailing-comma.sarif b/gcc/testsuite/sarif/invalid-json-array-with-trailing-comma.sarif
new file mode 100644
index 00000000000..05b74a81efc
--- /dev/null
+++ b/gcc/testsuite/sarif/invalid-json-array-with-trailing-comma.sarif
@@ -0,0 +1,6 @@
+[ 0, 1, 2, ] /* { dg-error "expected a JSON value but got '\\\]'" } */
+
+{ dg-begin-multiline-output "" }
+ 1 | [ 0, 1, 2, ]
+ | ^
+{ dg-end-multiline-output "" }
diff --git a/gcc/testsuite/sarif/invalid-json-bad-token.sarif b/gcc/testsuite/sarif/invalid-json-bad-token.sarif
new file mode 100644
index 00000000000..7756eef1add
--- /dev/null
+++ b/gcc/testsuite/sarif/invalid-json-bad-token.sarif
@@ -0,0 +1,6 @@
+ not a valid JSON file // { dg-error "invalid JSON token: unexpected character: 'n'" }
+
+{ dg-begin-multiline-output "" }
+ 1 | not a valid JSON file
+ | ^
+{ dg-end-multiline-output "" }
diff --git a/gcc/testsuite/sarif/invalid-json-object-missing-comma.sarif b/gcc/testsuite/sarif/invalid-json-object-missing-comma.sarif
new file mode 100644
index 00000000000..9d2bf9476b1
--- /dev/null
+++ b/gcc/testsuite/sarif/invalid-json-object-missing-comma.sarif
@@ -0,0 +1,7 @@
+{ "foo": "bar"
+ "baz": 42 } // { dg-error "expected ',' or '\}'; got string" }
+
+{ dg-begin-multiline-output "" }
+ 2 | "baz": 42 }
+ | ^~~~~
+{ dg-end-multiline-output "" }
diff --git a/gcc/testsuite/sarif/invalid-json-object-with-trailing-comma.sarif b/gcc/testsuite/sarif/invalid-json-object-with-trailing-comma.sarif
new file mode 100644
index 00000000000..e1aae9b350c
--- /dev/null
+++ b/gcc/testsuite/sarif/invalid-json-object-with-trailing-comma.sarif
@@ -0,0 +1,6 @@
+{ "foo": "bar", } /* { dg-error "expected string for object key after ','; got '\\\}'" } */
+
+{ dg-begin-multiline-output "" }
+ 1 | { "foo": "bar", }
+ | ^
+{ dg-end-multiline-output "" }
diff --git a/gcc/testsuite/sarif/invalid-sarif-bad-runs.sarif b/gcc/testsuite/sarif/invalid-sarif-bad-runs.sarif
new file mode 100644
index 00000000000..c5a26f05516
--- /dev/null
+++ b/gcc/testsuite/sarif/invalid-sarif-bad-runs.sarif
@@ -0,0 +1,7 @@
+{ "version": "2.1.0",
+ "runs": 42 } // { dg-error "expected sarifLog.runs to be 'null' or an array \\\[SARIF v2.1.0 §3.13.4\\\]" }
+
+/* { dg-begin-multiline-output "" }
+ 2 | "runs": 42 }
+ | ^~
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif/invalid-sarif-missing-arguments-for-placeholders.sarif b/gcc/testsuite/sarif/invalid-sarif-missing-arguments-for-placeholders.sarif
new file mode 100644
index 00000000000..c4354d4ef23
--- /dev/null
+++ b/gcc/testsuite/sarif/invalid-sarif-missing-arguments-for-placeholders.sarif
@@ -0,0 +1,14 @@
+{
+ "version": "2.1.0",
+ "runs": [{
+ "tool": { "driver": { "name": "example" } },
+ "results": [
+ { "message": { "text" : "the {0} {1} fox jumps over the {2} dog" } } /* { dg-error "message string contains placeholder '\\{0\\}' but message object has no 'arguments' property \\\[SARIF v2.1.0 §3.11.11\\\]" } */
+ ]
+ }]
+}
+
+/* { dg-begin-multiline-output "" }
+ 6 | { "message": { "text" : "the {0} {1} fox jumps over the {2} dog" } }
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif/invalid-sarif-no-runs.sarif b/gcc/testsuite/sarif/invalid-sarif-no-runs.sarif
new file mode 100644
index 00000000000..f142321642c
--- /dev/null
+++ b/gcc/testsuite/sarif/invalid-sarif-no-runs.sarif
@@ -0,0 +1,6 @@
+{ "version": "2.1.0" } // { dg-error "expected sarifLog object to have a 'runs' property \\\[SARIF v2.1.0 §3.13.4\\\]" }
+
+/* { dg-begin-multiline-output "" }
+ 1 | { "version": "2.1.0" }
+ | ^~~~~~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif/invalid-sarif-no-version.sarif b/gcc/testsuite/sarif/invalid-sarif-no-version.sarif
new file mode 100644
index 00000000000..771bd9c0c05
--- /dev/null
+++ b/gcc/testsuite/sarif/invalid-sarif-no-version.sarif
@@ -0,0 +1,6 @@
+{ } // { dg-error "expected sarifLog object to have a 'version' property \\\[SARIF v2.1.0 §3.13.2\\\]" }
+
+/* { dg-begin-multiline-output "" }
+ 1 | { }
+ | ^~~
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif/invalid-sarif-non-object-in-runs.sarif b/gcc/testsuite/sarif/invalid-sarif-non-object-in-runs.sarif
new file mode 100644
index 00000000000..4eeaaaa7b24
--- /dev/null
+++ b/gcc/testsuite/sarif/invalid-sarif-non-object-in-runs.sarif
@@ -0,0 +1,7 @@
+{ "version": "2.1.0",
+ "runs" : [42] } // { dg-error "expected element of sarifLog.runs array to be an object \\\[SARIF v2.1.0 §3.13.4\\\]" }
+
+/* { dg-begin-multiline-output "" }
+ 2 | "runs" : [42] }
+ | ^~
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif/invalid-sarif-not-an-object.sarif b/gcc/testsuite/sarif/invalid-sarif-not-an-object.sarif
new file mode 100644
index 00000000000..4743bad3ba3
--- /dev/null
+++ b/gcc/testsuite/sarif/invalid-sarif-not-an-object.sarif
@@ -0,0 +1,6 @@
+[ null ] // { dg-error "expected a sarifLog object as the top-level value \\\[SARIF v2.1.0 §3.1\\\]" }
+
+/* { dg-begin-multiline-output "" }
+ 1 | [ null ]
+ | ^~~~~~~~
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif/invalid-sarif-not-enough-arguments-for-placeholders.sarif b/gcc/testsuite/sarif/invalid-sarif-not-enough-arguments-for-placeholders.sarif
new file mode 100644
index 00000000000..e3eb5341110
--- /dev/null
+++ b/gcc/testsuite/sarif/invalid-sarif-not-enough-arguments-for-placeholders.sarif
@@ -0,0 +1,14 @@
+{
+ "version": "2.1.0",
+ "runs": [{
+ "tool": { "driver": { "name": "example" } },
+ "results": [
+ { "message": { "text" : "the {0} {1} fox jumps over the {2} dog", "arguments": ["quick", "brown"] } } /* { dg-error "not enough strings in 'arguments' array for placeholder '\\{2\\}' \\\[SARIF v2.1.0 §3.11.11\\\]" } */
+ ]
+ }]
+}
+
+/* { dg-begin-multiline-output "" }
+ 6 | { "message": { "text" : "the {0} {1} fox jumps over the {2} dog", "arguments": ["quick", "brown"] } }
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif/invalid-sarif-version-not-a-string.sarif b/gcc/testsuite/sarif/invalid-sarif-version-not-a-string.sarif
new file mode 100644
index 00000000000..0ffeb13626e
--- /dev/null
+++ b/gcc/testsuite/sarif/invalid-sarif-version-not-a-string.sarif
@@ -0,0 +1,6 @@
+{ "version" : 42 } // { dg-error "expected sarifLog.version to be a string \\\[SARIF v2.1.0 §3.13.2\\\]" }
+
+/* { dg-begin-multiline-output "" }
+ 1 | { "version" : 42 }
+ | ^~
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif/malformed-placeholder.sarif b/gcc/testsuite/sarif/malformed-placeholder.sarif
new file mode 100644
index 00000000000..72da185de0d
--- /dev/null
+++ b/gcc/testsuite/sarif/malformed-placeholder.sarif
@@ -0,0 +1,15 @@
+{
+ "version": "2.1.0",
+ "runs": [{
+ "tool": { "driver": { "name": "example" } },
+ "results": [
+ { "message": { "text" : "before {} after" }, /* { dg-error "unescaped '\\\{' within message string \\\[SARIF v2.1.0 §3.11.11\\\]" } */
+ "locations": [] }
+ ]
+ }]
+}
+
+/* { dg-begin-multiline-output "" }
+ 6 | { "message": { "text" : "before {} after" },
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/sarif/null-runs.sarif b/gcc/testsuite/sarif/null-runs.sarif
new file mode 100644
index 00000000000..5fc630eecb3
--- /dev/null
+++ b/gcc/testsuite/sarif/null-runs.sarif
@@ -0,0 +1,2 @@
+{ "version": "2.1.0",
+ "runs": null }
diff --git a/gcc/testsuite/sarif/roundtrip-signal-1.c.sarif b/gcc/testsuite/sarif/roundtrip-signal-1.c.sarif
new file mode 100644
index 00000000000..86f85383b89
--- /dev/null
+++ b/gcc/testsuite/sarif/roundtrip-signal-1.c.sarif
@@ -0,0 +1,398 @@
+/* { dg-options "-fdiagnostics-format=sarif-file -fallow-comments" } */
+
+{
+ "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
+ "runs": [
+ {
+ "results": [
+ {
+ "level": "warning",
+ "ruleId": "-Wanalyzer-unsafe-call-within-signal-handler",
+ "locations": [
+ {
+ "logicalLocations": [
+ {
+ "decoratedName": "custom_logger",
+ "kind": "function",
+ "name": "custom_logger",
+ "fullyQualifiedName": "custom_logger"
+ }
+ ],
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 13,
+ "snippet": {
+ "text": " fprintf(stderr, \"LOG: %s\", msg);"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 13,
+ "endColumn": 34,
+ "startColumn": 3
+ }
+ }
+ }
+ ],
+ "message": {
+ "text": "call to \u2018fprintf\u2019 from within signal handler"
+ },
+ "taxa": [
+ {
+ "id": "479",
+ "toolComponent": {
+ "name": "cwe"
+ }
+ }
+ ],
+ "codeFlows": [
+ {
+ "threadFlows": [
+ {
+ "locations": [
+ {
+ "nestingLevel": 1,
+ "location": {
+ "logicalLocations": [
+ {
+ "decoratedName": "main",
+ "kind": "function",
+ "name": "main",
+ "fullyQualifiedName": "main"
+ }
+ ],
+ "message": {
+ "text": "entry to \u2018main\u2019"
+ },
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 21,
+ "snippet": {
+ "text": "int main(int argc, const char *argv)\n"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 21,
+ "endColumn": 9,
+ "startColumn": 5
+ }
+ }
+ },
+ "kinds": [
+ "enter",
+ "function"
+ ]
+ },
+ {
+ "nestingLevel": 1,
+ "location": {
+ "logicalLocations": [
+ {
+ "decoratedName": "main",
+ "kind": "function",
+ "name": "main",
+ "fullyQualifiedName": "main"
+ }
+ ],
+ "message": {
+ "text": "registering \u2018handler\u2019 as signal handler"
+ },
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 25,
+ "snippet": {
+ "text": " signal(SIGINT, handler);\n"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 25,
+ "endColumn": 26,
+ "startColumn": 3
+ }
+ }
+ }
+ },
+ {
+ "nestingLevel": 0,
+ "location": {
+ "message": {
+ "text": "later on, when the signal is delivered to the process"
+ }
+ }
+ },
+ {
+ "nestingLevel": 1,
+ "location": {
+ "logicalLocations": [
+ {
+ "decoratedName": "handler",
+ "kind": "function",
+ "name": "handler",
+ "fullyQualifiedName": "handler"
+ }
+ ],
+ "message": {
+ "text": "entry to \u2018handler\u2019"
+ },
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 16,
+ "snippet": {
+ "text": "static void handler(int signum)\n"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 16,
+ "endColumn": 20,
+ "startColumn": 13
+ }
+ }
+ },
+ "kinds": [
+ "enter",
+ "function"
+ ]
+ },
+ {
+ "nestingLevel": 1,
+ "location": {
+ "logicalLocations": [
+ {
+ "decoratedName": "handler",
+ "kind": "function",
+ "name": "handler",
+ "fullyQualifiedName": "handler"
+ }
+ ],
+ "message": {
+ "text": "calling \u2018custom_logger\u2019 from \u2018handler\u2019"
+ },
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 18,
+ "snippet": {
+ "text": " custom_logger(\"got signal\");\n"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 18,
+ "endColumn": 30,
+ "startColumn": 3
+ }
+ }
+ },
+ "kinds": [
+ "call",
+ "function"
+ ]
+ },
+ {
+ "nestingLevel": 2,
+ "location": {
+ "logicalLocations": [
+ {
+ "decoratedName": "custom_logger",
+ "kind": "function",
+ "name": "custom_logger",
+ "fullyQualifiedName": "custom_logger"
+ }
+ ],
+ "message": {
+ "text": "entry to \u2018custom_logger\u2019"
+ },
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 11,
+ "snippet": {
+ "text": "void custom_logger(const char *msg)\n"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 11,
+ "endColumn": 19,
+ "startColumn": 6
+ }
+ }
+ },
+ "kinds": [
+ "enter",
+ "function"
+ ]
+ },
+ {
+ "nestingLevel": 2,
+ "location": {
+ "logicalLocations": [
+ {
+ "decoratedName": "custom_logger",
+ "kind": "function",
+ "name": "custom_logger",
+ "fullyQualifiedName": "custom_logger"
+ }
+ ],
+ "message": {
+ "text": "call to \u2018fprintf\u2019 from within signal handler"
+ },
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 13,
+ "snippet": {
+ "text": " fprintf(stderr, \"LOG: %s\", msg);\n"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 13,
+ "endColumn": 34,
+ "startColumn": 3
+ }
+ }
+ },
+ "kinds": [
+ "danger"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "artifacts": [
+ {
+ "location": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "sourceLanguage": "c",
+ "contents": {
+ "text": "/* Example of a bad call within a signal handler.\n 'handler' calls 'custom_logger' which calls 'fprintf', and 'fprintf' is\n not allowed from a signal handler. */\n\n\n#include <stdio.h>\n#include <signal.h>\n\nextern void body_of_program(void);\n\nvoid custom_logger(const char *msg)\n{\n fprintf(stderr, \"LOG: %s\", msg);\n}\n\nstatic void handler(int signum)\n{\n custom_logger(\"got signal\");\n}\n\nint main(int argc, const char *argv)\n{\n custom_logger(\"started\");\n\n signal(SIGINT, handler);\n\n body_of_program();\n\n custom_logger(\"stopped\");\n\n return 0;\n}\n"
+ }
+ }
+ ],
+ "tool": {
+ "driver": {
+ "fullName": "placeholder value for driver.fullName",
+ "name": "GNU C17",
+ "rules": [
+ {
+ "id": "-Wanalyzer-unsafe-call-within-signal-handler",
+ "helpUri": "https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html#index-Wanalyzer-unsafe-call-within-signal-handler"
+ }
+ ],
+ "informationUri": "https://gcc.gnu.org/gcc-13/",
+ "version": "placeholder value for driver.version"
+ }
+ },
+ "originalUriBaseIds": {
+ "PWD": {
+ "uri": "file:///home/david/coding/gcc-newgit-serialization/build/gcc/"
+ }
+ },
+ "taxonomies": [
+ {
+ "organization": "MITRE",
+ "name": "CWE",
+ "version": "4.7",
+ "shortDescription": {
+ "text": "The MITRE Common Weakness Enumeration"
+ },
+ "taxa": [
+ {
+ "id": "479",
+ "helpUri": "https://cwe.mitre.org/data/definitions/479.html"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "version": "2.1.0"
+}
+
+/* Verify that some JSON was written to a file with the expected name;
+ ideally we want as much of the data as possible to survive the round trip.
+
+ The indentation here reflects the expected hierarchy, though these tests
+ don't check for that, merely the string fragments we expect.
+
+ { dg-final { scan-sarif-file "\"version\": \"2.1.0\"" } }
+ { dg-final { scan-sarif-file "\"runs\": \\\[" } }
+ { dg-final { scan-sarif-file "\"artifacts\": \\\[" } }
+ { dg-final { scan-sarif-file "\"location\": " } }
+ { dg-final { scan-sarif-file "\"uri\": " } }
+
+ { dg-final { scan-sarif-file "\"sourceLanguage\": \"c\"" } }
+
+ { dg-final { scan-sarif-file "\"contents\": " { xfail *-*-* } } }
+ { dg-final { scan-sarif-file "\"text\": " } }
+ { dg-final { scan-sarif-file "\"tool\": " } }
+ { dg-final { scan-sarif-file "\"driver\": " } }
+ { dg-final { scan-sarif-file "\"name\": \"GNU C17\"" } }
+ { dg-final { scan-sarif-file "\"fullName\": \"placeholder value for driver.fullName\"" } }
+ { dg-final { scan-sarif-file "\"informationUri\": \"https://gcc.gnu.org/gcc-13/\"" } }
+ { dg-final { scan-sarif-file "\"version\": \"placeholder value for driver.version\"" } }
+ { dg-final { scan-sarif-file "\"results\": \\\[" } }
+ { dg-final { scan-sarif-file "\"level\": \"warning\"" } }
+ { dg-final { scan-sarif-file "\"ruleId\": \"-Wanalyzer-unsafe-call-within-signal-handler\"" } }
+ { dg-final { scan-sarif-file "\"locations\": \\\[" } }
+ { dg-final { scan-sarif-file "\"physicalLocation\": " } }
+ { dg-final { scan-sarif-file "\"contextRegion\": " } }
+ { dg-final { scan-sarif-file "\"artifactLocation\": " } }
+ { dg-final { scan-sarif-file "\"region\": " } }
+ { dg-final { scan-sarif-file "\"startLine\": 13" } }
+ { dg-final { scan-sarif-file "\"startColumn\": 3" } }
+ { dg-final { scan-sarif-file "\"endColumn\": 34" } }
+
+ { dg-final { scan-sarif-file "\"logicalLocations\": " } }
+ { dg-final { scan-sarif-file "\"decoratedName\": \"custom_logger\"" } }
+ { dg-final { scan-sarif-file "\"kind\": \"function\"" } }
+ { dg-final { scan-sarif-file "\"name\": \"custom_logger\"" } }
+ { dg-final { scan-sarif-file "\"fullyQualifiedName\": \"custom_logger\"" } }
+
+ { dg-final { scan-sarif-file "\"message\": " } }
+ { dg-final { scan-sarif-file "\"text\": \"call to \\u2018fprintf\\u2019 from within signal handler\"" } }
+
+ { dg-final { scan-sarif-file "\"codeFlows\": \\\[" } }
+ { dg-final { scan-sarif-file "\"threadFlows\": \\\[" } }
+ { dg-final { scan-sarif-file "\"nestingLevel\": 1" } }
+ { dg-final { scan-sarif-file "\"kinds\": \\\[\"enter\", \"function\"\\\]" { xfail *-*-* } } }
+
+ { dg-final { scan-sarif-file "\"nestingLevel\": 2" } }
+ { dg-final { scan-sarif-file "\"kinds\": \\\[\"danger\"\\\]" { xfail *-*-* } } }
+
+*/
+
+// TODO: fix the xfails
+// TODO: verify logical locations within the path
+// TODO: verify physical locations within the path
+// TODO: verify taxa
+// TODO: verify message text
+// etc
diff --git a/gcc/testsuite/sarif/sarif.exp b/gcc/testsuite/sarif/sarif.exp
new file mode 100644
index 00000000000..dcb1eb2bd58
--- /dev/null
+++ b/gcc/testsuite/sarif/sarif.exp
@@ -0,0 +1,50 @@
+# Copyright (C) 2004-2022 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3. If not see
+# <http://www.gnu.org/licenses/>.
+
+# GCC testsuite that uses the `dg.exp' driver.
+
+# Load support procs.
+load_lib sarif-dg.exp
+
+#load_lib dg.exp
+#load_lib prune.exp
+#load_lib target-supports.exp
+#load_lib gcc-defs.exp
+#load_lib timeout.exp
+#load_lib target-libpath.exp
+#load_lib gcc.exp
+#load_lib g++.exp
+#load_lib dejagnu.exp
+#load_lib prune.exp
+#load_lib gcc-defs.exp
+#load_lib timeout.exp
+#load_lib target-libpath.exp
+#load_lib target-supports.exp
+#load_lib gcc-dg.exp
+
+# If a testcase doesn't have special options, use these.
+global DEFAULT_SARIF_FLAGS
+if ![info exists DEFAULT_SARIF_FLAGS] then {
+ set DEFAULT_SARIF_FLAGS " -fallow-comments"
+}
+# Initialize `dg'.
+dg-init
+
+dg-runtest [lsort \
+ [glob -nocomplain $srcdir/$subdir/*.sarif ] ] "" $DEFAULT_SARIF_FLAGS
+
+# All done.
+dg-finish
diff --git a/gcc/testsuite/sarif/signal-1.c.sarif b/gcc/testsuite/sarif/signal-1.c.sarif
new file mode 100644
index 00000000000..a87ebb261d9
--- /dev/null
+++ b/gcc/testsuite/sarif/signal-1.c.sarif
@@ -0,0 +1,362 @@
+{
+ "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
+ "runs": [
+ {
+ "results": [
+ {
+ "level": "warning",
+ "ruleId": "-Wanalyzer-unsafe-call-within-signal-handler",
+ "locations": [
+ {
+ "logicalLocations": [
+ {
+ "decoratedName": "custom_logger",
+ "kind": "function",
+ "name": "custom_logger",
+ "fullyQualifiedName": "custom_logger"
+ }
+ ],
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 13,
+ "snippet": {
+ "text": " fprintf(stderr, \"LOG: %s\", msg);"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 13,
+ "endColumn": 34,
+ "startColumn": 3
+ }
+ }
+ }
+ ],
+ "message": {
+ "text": "call to \u2018fprintf\u2019 from within signal handler"
+ },
+ "taxa": [
+ {
+ "id": "479",
+ "toolComponent": {
+ "name": "cwe"
+ }
+ }
+ ],
+ "codeFlows": [
+ {
+ "threadFlows": [
+ {
+ "locations": [
+ {
+ "nestingLevel": 1,
+ "location": {
+ "logicalLocations": [
+ {
+ "decoratedName": "main",
+ "kind": "function",
+ "name": "main",
+ "fullyQualifiedName": "main"
+ }
+ ],
+ "message": {
+ "text": "entry to \u2018main\u2019"
+ },
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 21,
+ "snippet": {
+ "text": "int main(int argc, const char *argv)\n"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 21,
+ "endColumn": 9,
+ "startColumn": 5
+ }
+ }
+ },
+ "kinds": [
+ "enter",
+ "function"
+ ]
+ },
+ {
+ "nestingLevel": 1,
+ "location": {
+ "logicalLocations": [
+ {
+ "decoratedName": "main",
+ "kind": "function",
+ "name": "main",
+ "fullyQualifiedName": "main"
+ }
+ ],
+ "message": {
+ "text": "registering \u2018handler\u2019 as signal handler"
+ },
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 25,
+ "snippet": {
+ "text": " signal(SIGINT, handler);\n"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 25,
+ "endColumn": 26,
+ "startColumn": 3
+ }
+ }
+ }
+ },
+ {
+ "nestingLevel": 0,
+ "location": {
+ "message": {
+ "text": "later on, when the signal is delivered to the process"
+ }
+ }
+ },
+ {
+ "nestingLevel": 1,
+ "location": {
+ "logicalLocations": [
+ {
+ "decoratedName": "handler",
+ "kind": "function",
+ "name": "handler",
+ "fullyQualifiedName": "handler"
+ }
+ ],
+ "message": {
+ "text": "entry to \u2018handler\u2019"
+ },
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 16,
+ "snippet": {
+ "text": "static void handler(int signum)\n"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 16,
+ "endColumn": 20,
+ "startColumn": 13
+ }
+ }
+ },
+ "kinds": [
+ "enter",
+ "function"
+ ]
+ },
+ {
+ "nestingLevel": 1,
+ "location": {
+ "logicalLocations": [
+ {
+ "decoratedName": "handler",
+ "kind": "function",
+ "name": "handler",
+ "fullyQualifiedName": "handler"
+ }
+ ],
+ "message": {
+ "text": "calling \u2018custom_logger\u2019 from \u2018handler\u2019"
+ },
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 18,
+ "snippet": {
+ "text": " custom_logger(\"got signal\");\n"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 18,
+ "endColumn": 30,
+ "startColumn": 3
+ }
+ }
+ },
+ "kinds": [
+ "call",
+ "function"
+ ]
+ },
+ {
+ "nestingLevel": 2,
+ "location": {
+ "logicalLocations": [
+ {
+ "decoratedName": "custom_logger",
+ "kind": "function",
+ "name": "custom_logger",
+ "fullyQualifiedName": "custom_logger"
+ }
+ ],
+ "message": {
+ "text": "entry to \u2018custom_logger\u2019"
+ },
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 11,
+ "snippet": {
+ "text": "void custom_logger(const char *msg)\n"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 11,
+ "endColumn": 19,
+ "startColumn": 6
+ }
+ }
+ },
+ "kinds": [
+ "enter",
+ "function"
+ ]
+ },
+ {
+ "nestingLevel": 2,
+ "location": {
+ "logicalLocations": [
+ {
+ "decoratedName": "custom_logger",
+ "kind": "function",
+ "name": "custom_logger",
+ "fullyQualifiedName": "custom_logger"
+ }
+ ],
+ "message": {
+ "text": "call to \u2018fprintf\u2019 from within signal handler"
+ },
+ "physicalLocation": {
+ "contextRegion": {
+ "startLine": 13,
+ "snippet": {
+ "text": " fprintf(stderr, \"LOG: %s\", msg);\n"
+ }
+ },
+ "artifactLocation": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "region": {
+ "startLine": 13,
+ "endColumn": 34,
+ "startColumn": 3
+ }
+ }
+ },
+ "kinds": [
+ "danger"
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "artifacts": [
+ {
+ "location": {
+ "uri": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c",
+ "uriBaseId": "PWD"
+ },
+ "sourceLanguage": "c",
+ "contents": {
+ "text": "/* Example of a bad call within a signal handler.\n 'handler' calls 'custom_logger' which calls 'fprintf', and 'fprintf' is\n not allowed from a signal handler. */\n\n\n#include <stdio.h>\n#include <signal.h>\n\nextern void body_of_program(void);\n\nvoid custom_logger(const char *msg)\n{\n fprintf(stderr, \"LOG: %s\", msg);\n}\n\nstatic void handler(int signum)\n{\n custom_logger(\"got signal\");\n}\n\nint main(int argc, const char *argv)\n{\n custom_logger(\"started\");\n\n signal(SIGINT, handler);\n\n body_of_program();\n\n custom_logger(\"stopped\");\n\n return 0;\n}\n"
+ }
+ }
+ ],
+ "tool": {
+ "driver": {
+ "fullName": "some full name goes here",
+ "name": "GNU C17",
+ "rules": [
+ {
+ "id": "-Wanalyzer-unsafe-call-within-signal-handler",
+ "helpUri": "https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html#index-Wanalyzer-unsafe-call-within-signal-handler"
+ }
+ ],
+ "informationUri": "https://gcc.gnu.org/gcc-13/",
+ "version": "13.0.0 20220601"
+ }
+ },
+ "originalUriBaseIds": {
+ "PWD": {
+ "uri": "file:///home/david/coding/gcc-newgit-serialization/build/gcc/"
+ }
+ },
+ "taxonomies": [
+ {
+ "organization": "MITRE",
+ "name": "CWE",
+ "version": "4.7",
+ "shortDescription": {
+ "text": "The MITRE Common Weakness Enumeration"
+ },
+ "taxa": [
+ {
+ "id": "479",
+ "helpUri": "https://cwe.mitre.org/data/definitions/479.html"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "version": "2.1.0"
+}
+
+/* { dg-begin-multiline-output "" }
+../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c: In function 'custom_logger':
+../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c:13:3: warning: call to ‘fprintf’ from within signal handler [-Wanalyzer-unsafe-call-within-signal-handler]
+ 'main': events 1-2
+ |
+ |......
+ |
+ event 3
+ |
+ |sarif-replay:
+ | (3): later on, when the signal is delivered to the process
+ |
+ +--> 'handler': events 4-5
+ |
+ |
+ +--> 'custom_logger': events 6-7
+ |
+ |
+ { dg-end-multiline-output "" } */
+
+// TODO: fixup the src location
+// TODO: quote the source code
+// TODO: CWE
+// TODO: event messages
+// TODO: etc
diff --git a/gcc/testsuite/sarif/spec-example-1.sarif b/gcc/testsuite/sarif/spec-example-1.sarif
new file mode 100644
index 00000000000..97f409f4aa4
--- /dev/null
+++ b/gcc/testsuite/sarif/spec-example-1.sarif
@@ -0,0 +1,15 @@
+// Taken from SARIF v2.1.0, Appendix K.1: "Minimal valid SARIF log file"
+{
+ "version": "2.1.0",
+ "runs": [
+ {
+ "tool": {
+ "driver": {
+ "name": "CodeScanner"
+ }
+ },
+ "results": [
+ ]
+ }
+ ]
+}
diff --git a/gcc/testsuite/sarif/spec-example-2.sarif b/gcc/testsuite/sarif/spec-example-2.sarif
new file mode 100644
index 00000000000..352622dd5a5
--- /dev/null
+++ b/gcc/testsuite/sarif/spec-example-2.sarif
@@ -0,0 +1,74 @@
+/* Taken from SARIF v2.1.0, Appendix K.2: "Minimal recommended SARIF log
+ file with source information". */
+
+{
+ "version": "2.1.0",
+ "runs": [
+ {
+ "tool": {
+ "driver": {
+ "name": "CodeScanner",
+ "rules": [
+ {
+ "id": "C2001",
+ "fullDescription": {
+ "text": "A variable was used without being initialized. This can result in runtime errors such as null reference exceptions."
+ },
+ "messageStrings": {
+ "default": {
+ "text": "Variable \"{0}\" was used without being initialized."
+ }
+ }
+ }
+ ]
+ }
+ },
+ "artifacts": [
+ {
+ "location": {
+ "uri": "src/collections/list.cpp",
+ "uriBaseId": "SRCROOT"
+ },
+ "sourceLanguage": "c"
+ }
+ ],
+ "results": [
+ {
+ "ruleId": "C2001",
+ "ruleIndex": 0,
+ "message": {
+ "id": "default",
+ "arguments": [
+ "count"
+ ]
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "src/collections/list.cpp",
+ "uriBaseId": "SRCROOT",
+ "index": 0
+ },
+ "region": {
+ "startLine": 15
+ }
+ },
+ "logicalLocations": [
+ {
+ "fullyQualifiedName": "collections::list::add"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+
+/* { dg-begin-multiline-output "" }
+src/collections/list.cpp:15:1: warning: Variable "count" was used without being initialized. [C2001]
+ { dg-end-multiline-output "" } */
+
+// TODO: logical location
diff --git a/gcc/testsuite/sarif/spec-example-3.sarif b/gcc/testsuite/sarif/spec-example-3.sarif
new file mode 100644
index 00000000000..0a0018928e1
--- /dev/null
+++ b/gcc/testsuite/sarif/spec-example-3.sarif
@@ -0,0 +1,67 @@
+/* Taken from SARIF v2.1.0, Appendix K.3: "Minimal recommended SARIF log
+ file without source information". */
+
+{
+ "version": "2.1.0",
+ "runs": [
+ {
+ "tool": {
+ "driver": {
+ "name": "BinaryScanner"
+ }
+ },
+ "artifact": [
+ {
+ "location": {
+ "uri": "bin/example",
+ "uriBaseId": "BINROOT"
+ }
+ }
+ ],
+ "logicalLocations": [
+ {
+ "name": "Example",
+ "kind": "namespace"
+ },
+ {
+ "name": "Worker",
+ "fullyQualifiedName": "Example.Worker",
+ "kind": "type",
+ "parentIndex": 0
+ },
+ {
+ "name": "DoWork",
+ "fullyQualifiedName": "Example.Worker.DoWork",
+ "kind": "function",
+ "parentIndex": 1
+ }
+ ],
+ "results": [
+ {
+ "ruleId": "B6412",
+ "message": {
+ "text": "The insecure method \"Crypto.Sha1.Encrypt\" should not be used."
+ },
+ "level": "warning",
+ "locations": [
+ {
+ "logicalLocations": [
+ {
+ "fullyQualifiedName": "Example.Worker.DoWork",
+ "index": 2
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+
+/* { dg-begin-multiline-output "" }
+In function 'Example.Worker.DoWork':
+sarif-replay: warning: The insecure method "Crypto.Sha1.Encrypt" should not be used. [B6412]
+ { dg-end-multiline-output "" } */
+
+// TODO: the "sarif-replay: " prefix is unhelpful
diff --git a/gcc/testsuite/sarif/spec-example-4.sarif b/gcc/testsuite/sarif/spec-example-4.sarif
new file mode 100644
index 00000000000..6680d270905
--- /dev/null
+++ b/gcc/testsuite/sarif/spec-example-4.sarif
@@ -0,0 +1,758 @@
+/* Taken from SARIF v2.1.0, Appendix K.4: "Comprehensive SARIF file". */
+
+{
+ "version": "2.1.0",
+ "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
+ "runs": [
+ {
+ "automationId": {
+ "guid": "BC650830-A9FE-44CB-8818-AD6C387279A0",
+ "id": "Nightly code scan/2018-10-08"
+ },
+ "baselineGuid": "0A106451-C9B1-4309-A7EE-06988B95F723",
+ "runAggregates": [
+ {
+ "id": "Build/14.0.1.2/Release/20160716-13:22:18",
+ "correlationGuid": "26F138B6-6014-4D3D-B174-6E1ACE9439F3"
+ }
+ ],
+ "tool": {
+ "driver": {
+ "name": "CodeScanner",
+ "fullName": "CodeScanner 1.1 for Microsoft Windows (R) (en-US)",
+ "version": "2.1",
+ "semanticVersion": "2.1.0",
+ "dottedQuadFileVersion": "2.1.0.0",
+ "releaseDateUtc": "2019-03-17",
+ "organization": "Example Corporation",
+ "product": "Code Scanner",
+ "productSuite": "Code Quality Tools",
+ "shortDescription": {
+ "text": "A scanner for code."
+ },
+ "fullDescription": {
+ "text": "A really great scanner for all your code."
+ },
+ "properties": {
+ "copyright": "Copyright (c) 2017 by Example Corporation."
+ },
+ "globalMessageStrings": {
+ "variableDeclared": {
+ "text": "Variable \"{0}\" was declared here.",
+ "markdown": " Variable `{0}` was declared here."
+ }
+ },
+ "rules": [
+ {
+ "id": "C2001",
+ "deprecatedIds": [
+ "CA2000"
+ ],
+ "defaultConfiguration": {
+ "level": "error",
+ "rank": 95
+ },
+ "shortDescription": {
+ "text": "A variable was used without being initialized."
+ },
+ "fullDescription": {
+ "text": "A variable was used without being initialized. This can result in runtime errors such as null reference exceptions."
+ },
+ "messageStrings": {
+ "default": {
+ "text": "Variable \"{0}\" was used without being initialized. It was declared [here]({1}).",
+ "markdown": "Variable `{0}` was used without being initialized. It was declared [here]({1})."
+ }
+ }
+ }
+ ],
+ "notifications": [
+ {
+ "id": "start",
+ "shortDescription": {
+ "text": "The run started."
+ },
+ "messageStrings": {
+ "default": {
+ "text": "Run started."
+ }
+ }
+ },
+ {
+ "id": "end",
+ "shortDescription": {
+ "text": "The run ended."
+ },
+ "messageStrings": {
+ "default": {
+ "text": "Run ended."
+ }
+ }
+ }
+ ],
+ "language": "en-US"
+ },
+ "extensions": [
+ {
+ "name": "CodeScanner Security Rules",
+ "version": "3.1",
+ "rules": [
+ {
+ "id": "S0001",
+ "defaultConfiguration": {
+ "level": "error"
+ },
+ "shortDescription": {
+ "text": "Do not use weak cryptographic algorithms."
+ },
+ "messageStrings": {
+ "default": {
+ "text": "The cryptographic algorithm '{0}' should not be used."
+ }
+ }
+ }
+ ]
+ }
+ ]
+ },
+ "language": "en-US",
+ "versionControlProvenance": [
+ {
+ "repositoryUri": "https://github.com/example-corp/browser",
+ "revisionId": "5da53fbb2a0aaa12d648b73984acc9aac2e11c2a",
+ "mappedTo": {
+ "uriBaseId": "PROJECTROOT"
+ }
+ }
+ ],
+ "originalUriBaseIds": {
+ "PROJECTROOT": {
+ "uri": "file://build.example.com/work/"
+ },
+ "SRCROOT": {
+ "uri": " src/",
+ "uriBaseId": "PROJECTROOT"
+ },
+ "BINROOT": {
+ "uri": " bin/",
+ "uriBaseId": "PROJECTROOT"
+ }
+ },
+ "invocations": [
+ {
+ "commandLine": "CodeScanner @build/collections.rsp",
+ "responseFiles": [
+ {
+ "uri": "build/collections.rsp",
+ "uriBaseId": "SRCROOT",
+ "index": 0
+ }
+ ],
+ "startTimeUtc": "2016-07-16T14:18:25Z",
+ "endTimeUtc": "2016-07-16T14:19:01Z",
+ "machine": "BLD01",
+ "account": "buildAgent",
+ "processId": 1218,
+ "fileName": "/bin/tools/CodeScanner",
+ "workingDirectory": {
+ "uri": "file:///home/buildAgent/src"
+ },
+ "environmentVariables": {
+ "PATH": "/usr/local/bin:/bin:/bin/tools:/home/buildAgent/bin",
+ "HOME": "/home/buildAgent",
+ "TZ": "EST"
+ },
+ "toolConfigurationNotifications": [
+ {
+ "descriptor": {
+ "id": "UnknownRule"
+ },
+ "associatedRule": {
+ "ruleId": "ABC0001"
+ },
+ "level": "warning",
+ "message": {
+ "text": "Could not disable rule \"ABC0001\" because there is no rule with that id."
+ }
+ }
+ ],
+ "toolExecutionNotifications": [
+ {
+ "descriptor": {
+ "id": "CTN0001"
+ },
+ "level": "note",
+ "message": {
+ "text": "Run started."
+ }
+ },
+ {
+ "descriptor": {
+ "id": "CTN9999"
+ },
+ "associatedRule": {
+ "id": "C2001",
+ "index": 0
+ },
+ "level": "error",
+ "message": {
+ "text": "Exception evaluating rule \"C2001\". Rule disabled; run continues."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "crypto/hash.cpp",
+ "uriBaseId": "SRCROOT",
+ "index": 4
+ }
+ }
+ }
+ ],
+ "threadId": 52,
+ "timeUtc": "2016-07-16T14:18:43.119Z",
+ "exception": {
+ "kind": "ExecutionEngine.RuleFailureException",
+ "message": "Unhandled exception during rule evaluation.",
+ "stack": {
+ "frames": [
+ {
+ "location": {
+ "message": {
+ "text": "Exception thrown"
+ },
+ "logicalLocations": [
+ {
+ "fullyQualifiedName":
+ "Rules.SecureHashAlgorithmRule.Evaluate"
+ }
+ ],
+ "physicalLocation": {
+ "address": {
+ "offset": 4244988
+ }
+ }
+ },
+ "module": "RuleLibrary",
+ "threadId": 52
+ },
+ {
+ "location": {
+ "logicalLocations": [
+ {
+ "fullyQualifiedName":
+ "ExecutionEngine.Engine.EvaluateRule"
+ }
+ ],
+ "physicalLocation": {
+ "address": {
+ "offset": 4245514
+ }
+ }
+ },
+ "module": "ExecutionEngine",
+ "threadId": 52
+ }
+ ]
+ },
+ "innerExceptions": [
+ {
+ "kind": "System.ArgumentException",
+ "message": "length is < 0"
+ }
+ ]
+ }
+ },
+ {
+ "descriptor": {
+ "id": "CTN0002"
+ },
+ "level": "note",
+ "message": {
+ "text": "Run ended."
+ }
+ }
+ ],
+ "exitCode": 0,
+ "executionSuccessful": true
+ }
+ ],
+ "artifacts": [
+ {
+ "location": {
+ "uri": "build/collections.rsp",
+ "uriBaseId": "SRCROOT"
+ },
+ "mimeType": "text/plain",
+ "length": 81,
+ "contents": {
+ "text": "-input src/collections/*.cpp -log out/collections.sarif -rules all -disable C9999"
+ }
+ },
+ {
+ "location": {
+ "uri": "application/main.cpp",
+ "uriBaseId": "SRCROOT"
+ },
+ "sourceLanguage": "cplusplus",
+ "length": 1742,
+ "hashes": {
+ "sha-256": "cc8e6a99f3eff00adc649fee132ba80fe333ea5a"
+ }
+ },
+ {
+ "location": {
+ "uri": "collections/list.cpp",
+ "uriBaseId": "SRCROOT"
+ },
+ "sourceLanguage": "cplusplus",
+ "length": 980,
+ "hashes": {
+ "sha-256": "b13ce2678a8807ba0765ab94a0ecd394f869bc81"
+ }
+ },
+ {
+ "location": {
+ "uri": "collections/list.h",
+ "uriBaseId": "SRCROOT"
+ },
+ "sourceLanguage": "cplusplus",
+ "length": 24656,
+ "hashes": {
+ "sha-256": "849be119aaba4e9f88921a99e3036fb6c2a8144a"
+ }
+ },
+ {
+ "location": {
+ "uri": "crypto/hash.cpp",
+ "uriBaseId": "SRCROOT"
+ },
+ "sourceLanguage": "cplusplus",
+ "length": 1424,
+ "hashes": {
+ "sha-256": "3ffe2b77dz255cdf95f97d986d7a6ad8f287eaed"
+ }
+ },
+ {
+ "location": {
+ "uri": "app.zip",
+ "uriBaseId": "BINROOT"
+ },
+ "mimeType": "application/zip",
+ "length": 310450,
+ "hashes": {
+ "sha-256": "df18a5e74b6b46ddaa23ad7271ee2b7c5731cbe1"
+ }
+ },
+ {
+ "location": {
+ "uri": "/docs/intro.docx"
+ },
+ "mimeType":
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ "parentIndex": 5,
+ "offset": 17522,
+ "length": 4050
+ }
+ ],
+ "logicalLocations": [
+ {
+ "name": "add",
+ "fullyQualifiedName": "collections::list::add",
+ "decoratedName": "?add@list@collections@@QAEXH@Z",
+ "kind": "function",
+ "parentIndex": 1
+ },
+ {
+ "name": "list",
+ "fullyQualifiedName": "collections::list",
+ "kind": "type",
+ "parentIndex": 2
+ },
+ {
+ "name": "collections",
+ "kind": "namespace"
+ },
+ {
+ "name": "add_core",
+ "fullyQualfiedName": "collections::list::add_core",
+ "decoratedName": "?add_core@list@collections@@QAEXH@Z",
+ "kind": "function",
+ "parentIndex": 1
+ },
+ {
+ "fullyQualifiedName": "main",
+ "kind": "function"
+ }
+ ],
+ "results": [
+ {
+ "ruleId": "C2001",
+ "ruleIndex": 0,
+ "kind": "fail",
+ "level": "error",
+ "message": {
+ "id": "default",
+ "arguments": [
+ "ptr",
+ "0"
+ ]
+ },
+ "suppressions": [
+ {
+ "kind": "external",
+ "status": "accepted"
+ }
+ ],
+ "baselineState": "unchanged",
+ "rank": 95,
+ "analysisTarget": {
+ "uri": "collections/list.cpp",
+ "uriBaseId": "SRCROOT",
+ "index": 2
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "collections/list.h",
+ "uriBaseId": "SRCROOT",
+ "index": 3
+ },
+ "region": {
+ "startLine": 15,
+ "startColumn": 9,
+ "endLine": 15,
+ "endColumn": 10,
+ "charLength": 1,
+ "charOffset": 254,
+ "snippet": {
+ "text": "add_core(ptr, offset, val);\n return;"
+ }
+ }
+ },
+ "logicalLocations": [
+ {
+ "fullyQualifiedName": "collections::list::add",
+ "index": 0
+ }
+ ]
+ }
+ ],
+ "relatedLocations": [
+ {
+ "id": 0,
+ "message": {
+ "id": "variableDeclared",
+ "arguments": [
+ "ptr"
+ ]
+ },
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "collections/list.h",
+ "uriBaseId": "SRCROOT",
+ "index": 3
+ },
+ "region": {
+ "startLine": 8,
+ "startColumn": 5
+ }
+ },
+ "logicalLocations": [
+ {
+ "fullyQualifiedName": "collections::list::add",
+ "index": 0
+ }
+ ]
+ }
+ ],
+ "codeFlows": [
+ {
+ "message": {
+ "text": "Path from declaration to usage"
+ },
+
+ "threadFlows": [
+ {
+ "id": "thread-52",
+ "locations": [
+ {
+ "importance": "essential",
+ "location": {
+ "message": {
+ "text": "Variable \"ptr\" declared.",
+ "markdown": "Variable `ptr` declared."
+ },
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri":"collections/list.h",
+ "uriBaseId": "SRCROOT",
+ "index": 3
+ },
+ "region": {
+ "startLine": 15,
+ "snippet": {
+ "text": "int *ptr;"
+ }
+ }
+ },
+ "logicalLocations": [
+ {
+ "fullyQualifiedName": "collections::list::add",
+ "index": 0
+ }
+ ]
+ },
+ "module": "platform"
+ },
+ {
+ "state": {
+ "y": {
+ "text": "2"
+ },
+ "z": {
+ "text": "4"
+ },
+ "y + z": {
+ "text": "6"
+ },
+ "q": {
+ "text": "7"
+ }
+ },
+ "importance": "unimportant",
+ "location": {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri":"collections/list.h",
+ "uriBaseId": "SRCROOT",
+ "index": 3
+ },
+ "region": {
+ "startLine": 15,
+ "snippet": {
+ "text": "offset = (y + z) * q + 1;"
+ }
+ }
+ },
+ "logicalLocations": [
+ {
+ "fullyQualifiedName": "collections::list::add",
+ "index": 0
+ }
+ ],
+ "annotations": [
+ {
+ "startLine": 15,
+ "startColumn": 13,
+ "endColumn": 19,
+ "message": {
+ "text": "(y + z) = 42",
+ "markdown": "`(y + z) = 42`"
+ }
+ }
+ ]
+ },
+ "module": "platform"
+ },
+ {
+ "importance": "essential",
+ "location": {
+ "message": {
+ "text": "Uninitialized variable \"ptr\" passed to method \"add_core\".",
+ "markdown": "Uninitialized variable `ptr` passed to method `add_core`."
+ },
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri":"collections/list.h",
+ "uriBaseId": "SRCROOT",
+ "index": 3
+ },
+ "region": {
+ "startLine": 25,
+ "snippet": {
+ "text": "add_core(ptr, offset, val)"
+ }
+ }
+ },
+ "logicalLocations": [
+ {
+ "fullyQualifiedName": "collections::list::add",
+ "index": 0
+ }
+ ]
+ },
+ "module": "platform"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "stacks": [
+ {
+ "message": {
+ "text": "Call stack resulting from usage of uninitialized variable."
+ },
+ "frames": [
+ {
+ "location": {
+ "message": {
+ "text": "Exception thrown."
+ },
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "collections/list.h",
+ "uriBaseId": "SRCROOT",
+ "index": 3
+ },
+ "region": {
+ "startLine": 110,
+ "startColumn": 15
+ },
+ "address": {
+ "offset": 4229178
+ }
+ },
+ "logicalLocations": [
+ {
+ "fullyQualifiedName": "collections::list::add_core",
+ "index": 0
+ }
+ ]
+ },
+ "module": "platform",
+ "threadId": 52,
+ "parameters": [ "null", "0", "14" ]
+ },
+ {
+ "location": {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "collections/list.h",
+ "uriBaseId": "SRCROOT",
+ "index": 3
+ },
+ "region": {
+ "startLine": 43,
+ "startColumn": 15
+ },
+ "address": {
+ "offset": 4229268
+ }
+ },
+ "logicalLocations": [
+ {
+ "fullyQualifiedName": "collections::list::add",
+ "index": 0
+ }
+ ]
+ },
+ "module": "platform",
+ "threadId": 52,
+ "parameters": [ "14" ]
+ },
+ {
+ "location": {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "application/main.cpp",
+ "uriBaseId": "SRCROOT",
+ "index": 1
+ },
+ "region": {
+ "startLine": 28,
+ "startColumn": 9
+ },
+ "address": {
+ "offset": 4229836
+ }
+ },
+ "logicalLocations": [
+ {
+ "fullyQualifiedName": "main",
+ "index": 4
+ }
+ ]
+ },
+ "module": "application",
+ "threadId": 52
+ }
+ ]
+ }
+ ],
+ "addresses": [
+ {
+ "baseAddress": 4194304,
+ "fullyQualifiedName": "collections.dll",
+ "kind": "module",
+ "section": ".text"
+ },
+ {
+ "offset": 100,
+ "fullyQualifiedName": "collections.dll!collections::list::add",
+ "kind": "function",
+ "parentIndex": 0
+ },
+ {
+ "offset": 22,
+ "fullyQualifiedName": "collections.dll!collections::list::add+0x16",
+ "parentIndex": 1
+ }
+ ],
+ "fixes": [
+ {
+ "description": {
+ "text": "Initialize the variable to null"
+ },
+ "artifactChanges": [
+ {
+ "artifactLocation": {
+ "uri": "collections/list.h",
+ "uriBaseId": "SRCROOT",
+ "index": 3
+ },
+ "replacements": [
+ {
+ "deletedRegion": {
+ "startLine": 42
+ },
+ "insertedContent": {
+ "text": "A different line\n"
+ }
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "hostedViewerUri":
+ "https://www.example.com/viewer/3918d370-c636-40d8-bf23-8c176043a2df",
+ "workItemUris": [
+ "https://github.com/example/project/issues/42",
+ "https://github.com/example/project/issues/54"
+ ],
+ "provenance": {
+ "firstDetectionTimeUtc": "2016-07-15T14:20:42Z",
+ "firstDetectionRunGuid": "8F62D8A0-C14F-4516-9959-1A663BA6FB99",
+ "lastDetectionTimeUtc": "2016-07-16T14:20:42Z",
+ "lastDetectionRunGuid": "BC650830-A9FE-44CB-8818-AD6C387279A0",
+ "invocationIndex": 0
+ }
+ }
+ ]
+ }
+ ]
+}
+
+/* { dg-begin-multiline-output "" }
+collections/list.h: In function 'collections::list::add':
+collections/list.h:15:9: error: Variable "ptr" was used without being initialized. It was declared [here](0). [C2001]
+ 'collections::list::add': events 1-3
+ |
+ |......
+ |
+ { dg-end-multiline-output "" } */
+
+// TODO: what's up with the events?
diff --git a/gcc/testsuite/sarif/tutorial-example-foo.sarif b/gcc/testsuite/sarif/tutorial-example-foo.sarif
new file mode 100644
index 00000000000..8fa37ad4b42
--- /dev/null
+++ b/gcc/testsuite/sarif/tutorial-example-foo.sarif
@@ -0,0 +1,117 @@
+/* Adapted from https://github.com/microsoft/sarif-tutorials
+ samples/bad-eval-with-code-flow.sarif.
+ which is licensed under the Creative Commons Attribution 4.0 International Public License
+ and/or the MIT License. */
+
+{
+ "version": "2.1.0",
+ "runs": [
+ {
+ "tool": {
+ "driver": {
+ "name": "PythonScanner"
+ }
+ },
+ "results": [
+ {
+ "ruleId": "PY2335",
+ "message": {
+ "text": "Use of tainted variable 'raw_input' in the insecure function 'eval'."
+ },
+ "locations": [
+ {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "bad-eval-with-code-flow.py"
+ },
+ "region": {
+ "startLine": 8
+ }
+ }
+ }
+ ],
+ "codeFlows": [
+ {
+ "message": {
+ "text": "Tracing the path from user input to insecure usage."
+ },
+ "threadFlows": [
+ {
+ "locations": [
+ {
+ "location": {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "bad-eval-with-code-flow.py"
+ },
+ "region": {
+ "startLine": 3
+ }
+ }
+ },
+ "state": {
+ "expr": {
+ "text": "undef"
+ }
+ },
+ "nestingLevel": 0
+ },
+ {
+ "location": {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "bad-eval-with-code-flow.py"
+ },
+ "region": {
+ "startLine": 4
+ }
+ }
+ },
+ "state": {
+ "expr": {
+ "text": "42"
+ }
+ },
+ "nestingLevel": 0
+ },
+ {
+ "location": {
+ "physicalLocation": {
+ "artifactLocation": {
+ "uri": "bad-eval-with-code-flow.py"
+ },
+ "region": {
+ "startLine": 38
+ }
+ }
+ },
+ "state": {
+ "raw_input": {
+ "text": "42"
+ }
+ },
+ "nestingLevel": 1
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+}
+
+/* { dg-begin-multiline-output "" }
+bad-eval-with-code-flow.py:8:1: warning: Use of tainted variable 'raw_input' in the insecure function 'eval'. [PY2335]
+ events 1-2
+ |
+ |
+ +--> event 3
+ |
+ |
+ { dg-end-multiline-output "" } */
+
+// TODO: logical locations?
+// TODO: fix showing the source code
--
2.26.3
next prev parent reply other threads:[~2022-06-22 22:34 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-06-22 22:34 [PATCH 00/12] RFC: Replay of serialized diagnostics David Malcolm
2022-06-22 22:34 ` [PATCH 01/12] diagnostics: add ability to associate diagnostics with rules from coding standards David Malcolm
2022-06-23 19:04 ` David Malcolm
2022-06-22 22:34 ` [PATCH 02/12] diagnostics: associate rules with plugins in SARIF output David Malcolm
2022-06-22 22:34 ` [PATCH 03/12] Add more emit_diagnostic overloads David Malcolm
2022-06-23 16:39 ` Joseph Myers
2022-06-22 22:34 ` [PATCH 04/12] json: add json parsing support David Malcolm
2022-06-22 22:34 ` [PATCH 05/12] Placeholder libcpp fixups David Malcolm
2022-06-22 22:34 ` [PATCH 06/12] prune.exp: move multiline-handling to before other pruning David Malcolm
2022-06-22 22:34 ` [PATCH 07/12] Add deferred-locations.h/cc David Malcolm
2022-06-22 22:34 ` [PATCH 08/12] Add json-reader.h/cc David Malcolm
2022-06-22 22:34 ` [PATCH 09/12] Add json frontend David Malcolm
2022-06-22 22:34 ` David Malcolm [this message]
2022-06-22 22:34 ` [PATCH 11/12] Fixups to diagnostic-format-sarif.cc David Malcolm
2022-06-22 22:34 ` [PATCH 12/12] Work-in-progress of path remapping David Malcolm
2022-07-08 18:40 ` [PATCH 00/12] RFC: Replay of serialized diagnostics David Malcolm
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220622223447.2462880-11-dmalcolm@redhat.com \
--to=dmalcolm@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).