From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 5803A383067E for ; Wed, 22 Jun 2022 22:34:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 5803A383067E Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-417-3r4UIOlAMMGtTbfabJOdgQ-1; Wed, 22 Jun 2022 18:34:50 -0400 X-MC-Unique: 3r4UIOlAMMGtTbfabJOdgQ-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 8FCE81C06904 for ; Wed, 22 Jun 2022 22:34:50 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.2.17.61]) by smtp.corp.redhat.com (Postfix) with ESMTP id 61F131121314; Wed, 22 Jun 2022 22:34:50 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org Subject: [PATCH 10/12] Add sarif frontend Date: Wed, 22 Jun 2022 18:34:45 -0400 Message-Id: <20220622223447.2462880-11-dmalcolm@redhat.com> In-Reply-To: <20220622223447.2462880-1-dmalcolm@redhat.com> References: <20220622223447.2462880-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.78 on 10.11.54.3 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-12.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 22 Jun 2022 22:35:05 -0000 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 --- 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 +# . + +# 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 +# . + +# 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 +. */ + +/* 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 +; . + +; 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 . + +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 +. */ + +// 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 . + Contributed by David Malcolm . + +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 +. */ + +// 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 (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 (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 (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 (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 (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 m_phys_loc_map; + // TODO: delete this in dtor + + int m_pass; + + hash_map 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 (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" + " % or an array"); + break; + case json::JSON_NULL: + /* Nothing to do. */ + return; + case json::JSON_ARRAY: + { + json::array *runs_arr = as_a (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" + " % or an array"); + break; + case json::JSON_NULL: + /* Nothing to do. */ + return; + case json::JSON_ARRAY: + { + json::array *results_arr = as_a (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 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 . + Contributed by David Malcolm . + +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 +. */ + +#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 +# . + +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 +# . + +# 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 \n#include \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 +# . + +# 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 \n#include \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