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.133.124]) by sourceware.org (Postfix) with ESMTPS id C477B383065E for ; Wed, 22 Jun 2022 22:34:51 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org C477B383065E Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-394-QYK4UwA7PkWEgdgl3KrA2A-1; Wed, 22 Jun 2022 18:34:50 -0400 X-MC-Unique: QYK4UwA7PkWEgdgl3KrA2A-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 5275D89C7DE 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 30DA21121314; Wed, 22 Jun 2022 22:34:50 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org Subject: [PATCH 09/12] Add json frontend Date: Wed, 22 Jun 2022 18:34:44 -0400 Message-Id: <20220622223447.2462880-10-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.1 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:34:59 -0000 This patch adds a new json frontend: json-replayer, which the gcc driver can invoke on .json files saved with -fdiagnostics-format=json-file. gcc/ChangeLog: * json/Make-lang.in: New file. * json/config-lang.in: New file. * json/json-frontend.cc: New file. * json/json-replay.cc: New file. * json/json-replay.h: New file. * json/lang-specs.h: New file. * json/lang.opt: New file. gcc/testsuite/ChangeLog: * json/invalid-json-array-missing-comma.json: New test. * json/invalid-json-array-with-trailing-comma.json: New test. * json/invalid-json-bad-token.json: New test. * json/invalid-json-object-missing-comma.json: New test. * json/invalid-json-object-with-trailing-comma.json: New test. * json/invalid-jsondump-diag-not-an-object.json: New test. * json/invalid-jsondump-kind-not-a-string.json: New test. * json/invalid-jsondump-not-an-array.json: New test. * json/json.exp: New test. * json/signal-1.c.json: New test. * lib/json-dg.exp: New test. * lib/json.exp: New test. Signed-off-by: David Malcolm --- gcc/json/Make-lang.in | 131 ++++ gcc/json/config-lang.in | 34 + gcc/json/json-frontend.cc | 176 +++++ gcc/json/json-replay.cc | 614 ++++++++++++++++++ gcc/json/json-replay.h | 26 + gcc/json/lang-specs.h | 26 + gcc/json/lang.opt | 31 + .../invalid-json-array-missing-comma.json | 6 + ...nvalid-json-array-with-trailing-comma.json | 6 + .../json/invalid-json-bad-token.json | 6 + .../invalid-json-object-missing-comma.json | 7 + ...valid-json-object-with-trailing-comma.json | 6 + .../invalid-jsondump-diag-not-an-object.json | 6 + .../invalid-jsondump-kind-not-a-string.json | 20 + .../json/invalid-jsondump-not-an-array.json | 6 + gcc/testsuite/json/json.exp | 50 ++ gcc/testsuite/json/signal-1.c.json | 131 ++++ gcc/testsuite/lib/json-dg.exp | 233 +++++++ gcc/testsuite/lib/json.exp | 36 + 19 files changed, 1551 insertions(+) create mode 100644 gcc/json/Make-lang.in create mode 100644 gcc/json/config-lang.in create mode 100644 gcc/json/json-frontend.cc create mode 100644 gcc/json/json-replay.cc create mode 100644 gcc/json/json-replay.h create mode 100644 gcc/json/lang-specs.h create mode 100644 gcc/json/lang.opt create mode 100644 gcc/testsuite/json/invalid-json-array-missing-comma.json create mode 100644 gcc/testsuite/json/invalid-json-array-with-trailing-comma.json create mode 100644 gcc/testsuite/json/invalid-json-bad-token.json create mode 100644 gcc/testsuite/json/invalid-json-object-missing-comma.json create mode 100644 gcc/testsuite/json/invalid-json-object-with-trailing-comma.json create mode 100644 gcc/testsuite/json/invalid-jsondump-diag-not-an-object.json create mode 100644 gcc/testsuite/json/invalid-jsondump-kind-not-a-string.json create mode 100644 gcc/testsuite/json/invalid-jsondump-not-an-array.json create mode 100644 gcc/testsuite/json/json.exp create mode 100644 gcc/testsuite/json/signal-1.c.json create mode 100644 gcc/testsuite/lib/json-dg.exp create mode 100644 gcc/testsuite/lib/json.exp diff --git a/gcc/json/Make-lang.in b/gcc/json/Make-lang.in new file mode 100644 index 00000000000..a62d028f41a --- /dev/null +++ b/gcc/json/Make-lang.in @@ -0,0 +1,131 @@ +# Make-lang.in -- Top level -*- makefile -*- fragment for gcc JSON "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 json in LANGUAGES. +json: json-replay$(exeext) + +.PHONY: json + +JSON_OBJS = json/json-frontend.o json/json-replay.o \ + attribs.o deferred-locations.o json-reader.o +json_OBJS = $(JSON_OBJS) + +json-replay$(exeext): $(JSON_OBJS) $(BACKEND) $(LIBDEPS) + @$(call LINK_PROGRESS,$(INDEX.json),start) + +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \ + $(JSON_OBJS) $(BACKEND) $(LIBS) $(BACKENDLIBS) + @$(call LINK_PROGRESS,$(INDEX.json),end) + +# Build hooks. + +json.all.cross: +json.start.encap: +json.rest.encap: + +json.info: +json.man: + +lang_checks += check-json + +# No json-specific selftests +selftest-json: + +# Install hooks. + +json.install-common: installdirs + -rm -f $(DESTDIR)$(bindir)/$(GCCJSON_INSTALL_NAME)$(exeext) + $(INSTALL_PROGRAM) gccjson$(exeext) $(DESTDIR)$(bindir)/$(GCCJSON_INSTALL_NAME)$(exeext) + -if test -f json-replay$(exeext); then \ + if test -f gccjson-cross$(exeext); then \ + :; \ + else \ + rm -f $(DESTDIR)$(bindir)/$(GCCJSON_TARGET_INSTALL_NAME)$(exeext); \ + ( cd $(DESTDIR)$(bindir) && \ + $(LN) $(GCCJSON_INSTALL_NAME)$(exeext) $(GCCJSON_TARGET_INSTALL_NAME)$(exeext) ); \ + fi; \ + fi + +json.install-plugin: + +json.install-info: $(DESTDIR)$(infodir)/gccjson.info + +json.install-pdf: doc/gccjson.pdf + @$(NORMAL_INSTALL) + test -z "$(pdfdir)" || $(mkinstalldirs) "$(DESTDIR)$(pdfdir)/gcc" + @for p in doc/gccjson.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 + +json.install-html: $(build_htmldir)/json + @$(NORMAL_INSTALL) + test -z "$(htmldir)" || $(mkinstalldirs) "$(DESTDIR)$(htmldir)" + @for p in $(build_htmldir)/json; 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 + +json.install-man: $(DESTDIR)$(man1dir)/$(GCCJSON_INSTALL_NAME)$(man1ext) + + +json.uninstall: + rm -rf $(DESTDIR)$(bindir)/$(GCCJSON_INSTALL_NAME)$(exeext) + rm -rf $(DESTDIR)$(man1dir)/$(GCCJSON_INSTALL_NAME)$(man1ext) + rm -rf $(DESTDIR)$(bindir)/$(GCCJSON_TARGET_INSTALL_NAME)$(exeext) + rm -rf $(DESTDIR)$(infodir)/gccjson.info* + +# Clean hooks. + +json.mostlyclean: + -rm -f json/*$(objext) + -rm -f json/*$(coverageexts) + -rm -f gccjson$(exeext) gccjson-cross$(exeext) json-replay$(exeext) +json.clean: +json.distclean: +json.maintainer-clean: + -rm -f $(docobjdir)/gccjson.1 + +# Stage hooks. + +json.stage1: stage1-start + -mv json/*$(objext) stage1/json +json.stage2: stage2-start + -mv json/*$(objext) stage2/json +json.stage3: stage3-start + -mv json/*$(objext) stage3/json +json.stage4: stage4-start + -mv json/*$(objext) stage4/json +json.stageprofile: stageprofile-start + -mv json/*$(objext) stageprofile/json +json.stagefeedback: stagefeedback-start + -mv json/*$(objext) stagefeedback/json diff --git a/gcc/json/config-lang.in b/gcc/json/config-lang.in new file mode 100644 index 00000000000..c1b3593570c --- /dev/null +++ b/gcc/json/config-lang.in @@ -0,0 +1,34 @@ +# config-lang.in -- Top level configure fragment for gcc JSON "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="json" + +compilers="json-replay\$(exeext)" + +gtfiles="\$(srcdir)/json/json-frontend.cc" + +# Build by default. +build_by_default="yes" diff --git a/gcc/json/json-frontend.cc b/gcc/json/json-frontend.cc new file mode 100644 index 00000000000..edf72282d96 --- /dev/null +++ b/gcc/json/json-frontend.cc @@ -0,0 +1,176 @@ +/* The dummy "frontend" for re-emitting diagnostics saved in JSON 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 +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "debug.h" +#include "langhooks.h" +#include "langhooks-def.h" +#include "json/json-replay.h" +#include "opts.h" +#include "diagnostic.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 +json_langhook_init (void) +{ + replay_json (main_input_filename); + return false; +} + +static unsigned int +json_langhook_option_lang_mask (void) +{ + return CL_JSON; +} + +static bool +json_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 & json_langhook_option_lang_mask ()) + break; + result = false; + } + + JSON_handle_option_auto (&global_options, &global_options_set, + scode, arg, value, + json_langhook_option_lang_mask (), kind, + loc, handlers, global_dc); + + return result; +} + +static tree +json_langhook_type_for_mode (machine_mode, int) +{ + gcc_unreachable (); + return NULL_TREE; +} + +static bool +json_langhook_global_bindings_p (void) +{ + return true; +} + +static tree +json_langhook_pushdecl (tree decl ATTRIBUTE_UNUSED) +{ + gcc_unreachable (); +} + +static tree +json_langhook_getdecls (void) +{ + return NULL; +} +#undef LANG_HOOKS_NAME +#define LANG_HOOKS_NAME "json" + +#undef LANG_HOOKS_INIT +#define LANG_HOOKS_INIT json_langhook_init + +#undef LANG_HOOKS_OPTION_LANG_MASK +#define LANG_HOOKS_OPTION_LANG_MASK json_langhook_option_lang_mask + +#undef LANG_HOOKS_HANDLE_OPTION +#define LANG_HOOKS_HANDLE_OPTION json_langhook_handle_option + +#undef LANG_HOOKS_TYPE_FOR_MODE +#define LANG_HOOKS_TYPE_FOR_MODE json_langhook_type_for_mode + +#undef LANG_HOOKS_GLOBAL_BINDINGS_P +#define LANG_HOOKS_GLOBAL_BINDINGS_P json_langhook_global_bindings_p + +#undef LANG_HOOKS_PUSHDECL +#define LANG_HOOKS_PUSHDECL json_langhook_pushdecl + +#undef LANG_HOOKS_GETDECLS +#define LANG_HOOKS_GETDECLS json_langhook_getdecls + +#undef LANG_HOOKS_DEEP_UNSHARING +#define LANG_HOOKS_DEEP_UNSHARING true + +struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER; + +#include "gt-json-json-frontend.h" +#include "gtype-json.h" diff --git a/gcc/json/json-replay.cc b/gcc/json/json-replay.cc new file mode 100644 index 00000000000..d24d2b63e15 --- /dev/null +++ b/gcc/json/json-replay.cc @@ -0,0 +1,614 @@ +/* Re-emitting diagnostics saved in JSON 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 +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "diagnostic.h" +#include "diagnostic-metadata.h" +#include "diagnostic-path.h" +#include "line-map.h" +#include "stringpool.h" +#include "gcc-rich-location.h" +#include "json-parsing.h" +#include "json-reader.h" +#include "deferred-locations.h" +#include "diagnostic-client-data-hooks.h" +#include "json/json-replay.h" + +class json_replayer; + +/* Concrete subclass of diagnostic_client_data_hooks, for + use when replaying a json file. + + Takes ownership of the json_replayer and toplevel json::value objects. */ + +class json_replayer_diagnostic_client_data_hooks + : public diagnostic_client_data_hooks +{ + public: + json_replayer_diagnostic_client_data_hooks (json_replayer *replayer) + : m_replayer (replayer), + m_toplevel_jv (NULL) + {} + + ~json_replayer_diagnostic_client_data_hooks (); + + const client_version_info *get_any_version_info () const final override + { + /* The JSON dump doesn't contain any version info. */ + return NULL; + } + const logical_location *get_current_logical_location () const final override + { + return NULL; // TODO + } + const char * + maybe_get_sarif_source_language (const char *) const final override + { + return NULL; + } + + void stash (json::value *toplevel_jv) + { + m_toplevel_jv = toplevel_jv; + } + + json_replayer *m_replayer; + json::value *m_toplevel_jv; +}; + +/* A bundle of state for replaying a GCC diagnostic json file. */ + +class json_replayer : public json_reader +{ +public: + json_replayer (const char *filename) : json_reader (filename) {} + ~json_replayer (); + + void emit_json_as_diagnostics (json::value *jv); + + /* Get the value of property ATTR_NAME within OBJ, + exiting with an error if it is not present. */ + json::value * + get_required_attr (json::object *obj, const char *attr_name) + { + json::value *attr_val = obj->get (attr_name); + if (!attr_val) + fatal_error (obj, + "expected a %qs within object", attr_name); + return attr_val; + } + + /* Get the value of optional property ATTR_NAME within OBJ, + exiting with an error if it is not a string. */ + const char * + get_optional_string_attr (json::object *obj, const char *attr_name) + { + json::value *attr_val = obj->get (attr_name); + if (!attr_val) + return NULL; + json::string *attr_str = dyn_cast (attr_val); + if (!attr_str) + fatal_error (attr_val, + "expected the value of %qs to be a string", attr_name); + return attr_str->get_string (); + } + + /* Get the value of property ATTR_NAME within OBJ, + exiting with an error if it is not present or is not a string. */ + const char * + get_required_string_attr (json::object *obj, const char *attr_name) + { + json::value *attr_val = get_required_attr (obj, attr_name); + json::string *attr_str = dyn_cast (attr_val); + if (!attr_str) + fatal_error (attr_val, + "expected the value of %qs to be a string", attr_name); + return attr_str->get_string (); + } + + /* Get the value of optional property ATTR_NAME within OBJ, + exiting with an error if it is not an object. */ + json::object * + get_optional_object_attr (json::object *obj, const char *attr_name) + { + json::value *attr_val = obj->get (attr_name); + if (!attr_val) + return NULL; + json::object *attr_obj = dyn_cast (attr_val); + if (!attr_obj) + fatal_error (attr_val, + "expected the value of %qs to be an object", attr_name); + return attr_obj; + } + + /* Get the value of property ATTR_NAME within OBJ, + exiting with an error if it is not present or is not an object. */ + json::object * + get_required_object_attr (json::object *obj, const char *attr_name) + { + json::value *attr_val = get_required_attr (obj, attr_name); + json::object *attr_obj = dyn_cast (attr_val); + if (!attr_obj) + fatal_error (attr_val, + "expected the value of %qs to be an object", attr_name); + return attr_obj; + } + + /* Get the value of optional property ATTR_NAME within OBJ, + exiting with an error if it is not an array. */ + json::array * + get_optional_array_attr (json::object *obj, const char *attr_name) + { + json::value *attr_val = obj->get (attr_name); + if (!attr_val) + return NULL; + json::array *attr_arr = dyn_cast (attr_val); + if (!attr_arr) + fatal_error (attr_val, + "expected the value of %qs to be an array", attr_name); + return attr_arr; + } + + /* Get the value of property ATTR_NAME within OBJ, + exiting with an error if it is not present or is not an integer. */ + long + get_required_long_attr (json::object *obj, const char *attr_name) + { + json::value *attr_val = get_required_attr (obj, attr_name); + json::integer_number *attr_int + = dyn_cast (attr_val); + if (!attr_int) + fatal_error (attr_val, + "expected the value of %qs to be an integer number", + attr_name); + return attr_int->get (); + } + + /* If OBJ has optional property ATTR_NAME within OBJ and it is an integer, + return true and write its value to *OUT. + If it is not present, return false. + If it is not an integer, exit with an error. */ + bool + get_optional_long_attr (json::object *obj, const char *attr_name, long *out) + { + json::value *attr_val = obj->get (attr_name); + if (!attr_val) + return false; + json::integer_number *attr_int + = dyn_cast (attr_val); + if (!attr_int) + fatal_error (attr_val, + "expected the value of %qs to be an integer number", + attr_name); + *out = attr_int->get (); + return true; + } + + json::object * + require_object (json::value *jv) + { + if (json::object *obj = dyn_cast (jv)) + return obj; + fatal_error (jv, "expected an object"); + return NULL; + } + + location_t + get_required_expanded_location_attr (json::object *obj, const char *attr_name, + deferred_locations *deferred_locs, + int pass); + + location_t + get_optional_expanded_location_attr (json::object *obj, const char *attr_name, + deferred_locations *deferred_locs, + int pass); +private: + void emit_diag_obj (json::object *diag_obj, + deferred_locations *deferred_locs, + int pass); + location_t + get_location_from_expanded_location_obj (json::object *loc_obj, + deferred_locations *deferred_locs, + int pass); + + diagnostic_t get_diagnostic_kind (json::object *diag_obj); + + hash_map m_map_loc_obj_to_loc_t; +}; + +/* class json_replayer_diagnostic_client_data_hooks + : public diagnostic_client_data_hooks. */ + +json_replayer_diagnostic_client_data_hooks:: +~json_replayer_diagnostic_client_data_hooks () +{ + delete m_replayer; + delete m_toplevel_jv; +} + +/* class json_replayer : public json_reader. */ + +json_replayer::~json_replayer () +{ + for (auto iter : m_map_loc_obj_to_loc_t) + delete iter.second; +} + +/* In pass 0, defer the creation of a location_t for OBJ (originally created + by json_from_expanded_location). + In pass 1, look up the location_t that was created. */ + +location_t +json_replayer:: +get_location_from_expanded_location_obj (json::object *loc_obj, + deferred_locations *deferred_locs, + int pass) +{ + expanded_location exp_loc = {NULL, 0, 0, NULL, false}; + exp_loc.file = get_optional_string_attr (loc_obj, "file"); + long line; + if (get_optional_long_attr (loc_obj, "line", &line)) + exp_loc.line = line; + long column; + if (get_optional_long_attr (loc_obj, "column", &column)) + exp_loc.column = column; + if (pass) + return **m_map_loc_obj_to_loc_t.get (loc_obj); + else + { + location_t *loc = new location_t; + deferred_locs->add_location (exp_loc, loc); + m_map_loc_obj_to_loc_t.put (loc_obj, loc); + return UNKNOWN_LOCATION; + } +} + +/* Get the value of property ATTR_NAME within OBJ, + exiting with an error if it is not present or not a location. + In pass 0, defer the creation of a location_t for the value (originally + created by json_from_expanded_location). + In pass 1, look up the location_t that was created. */ + +location_t +json_replayer:: +get_required_expanded_location_attr (json::object *obj, const char *attr_name, + deferred_locations *deferred_locs, + int pass) +{ + json::object *loc_obj = get_required_object_attr (obj, attr_name); + return get_location_from_expanded_location_obj (loc_obj, deferred_locs, pass); +} + +/* As get_required_expanded_location_attr, but return UNKNOWN_LOCATION + if ATTR_NAME is not present within OBJ. */ + +location_t +json_replayer:: +get_optional_expanded_location_attr (json::object *obj, const char *attr_name, + deferred_locations *deferred_locs, + int pass) +{ + json::object *loc_obj = get_optional_object_attr (obj, attr_name); + if (!loc_obj) + return UNKNOWN_LOCATION; + return get_location_from_expanded_location_obj (loc_obj, deferred_locs, pass); +} + +/* Get the diagnostic_t kind for DIAG_OBJ. */ + +diagnostic_t +json_replayer::get_diagnostic_kind (json::object *diag_obj) +{ + const char *kind_str = get_required_string_attr (diag_obj, "kind"); + + static const char *const diagnostic_kind_text[] = { +#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T), +#include "diagnostic.def" +#undef DEFINE_DIAGNOSTIC_KIND + "must-not-happen" + }; + for (unsigned i = 0; i < ARRAY_SIZE (diagnostic_kind_text); i++) + { + /* Compare, without the trailing ": ". */ + const char *kind_text = diagnostic_kind_text[i]; + size_t len = strlen (kind_text); + if (len <= 2) + continue; + gcc_assert (kind_text[len - 2] == ':'); + gcc_assert (kind_text[len - 1] == ' '); + if (0 == strncmp (kind_text, kind_str, len - 2) + && kind_str[len - 2] == '\0') + return static_cast (i); + } + fatal_error (diag_obj, + "unrecognized value for %qs: %qs", "kind", kind_str); +} + +static hash_map map_id_to_fndecl; + +/* Create a placeholder void -> void function declaration for a function + named FUNC_STR. */ + +static tree +make_placeholder_fndecl (const char *func_str) +{ + tree id = get_identifier (func_str); + if (tree *slot = map_id_to_fndecl.get (id)) + return *slot; + tree fndecl = build_fn_decl (func_str, NULL_TREE/*fn_type*/); + map_id_to_fndecl.put (id, fndecl); + return fndecl; +} + +/* Custom subclass of rich_location, relating to replaying a specific + diagnostic serialized to JSON. */ + +class json_rich_location : public rich_location +{ +public: + json_rich_location (json_replayer *replayer, + json::object *diag_obj, deferred_locations *deferred_locs, + int pass) + : rich_location (line_table, UNKNOWN_LOCATION) + { + json::value *locations_val + = replayer->get_required_attr (diag_obj, "locations"); + json::array *locations_arr = dyn_cast (locations_val); + if (!locations_arr) + replayer->fatal_error (locations_val, + "expected an array for the value of %qs", + "locations"); + m_ranges.truncate (0); + for (auto loc_iter : *locations_arr) + { + json::object *loc_obj = dyn_cast (loc_iter); + if (!loc_obj) + replayer->fatal_error (loc_iter, + "expected an object within the %qs array", + "locations"); + /* Compare with diagnostic-format-json.cc:json_from_location_range. */ + location_t caret_loc + = replayer->get_required_expanded_location_attr (loc_obj, "caret", + deferred_locs, pass); + location_t start_loc + = replayer->get_optional_expanded_location_attr (loc_obj, "start", + deferred_locs, pass); + location_t finish_loc + = replayer->get_optional_expanded_location_attr (loc_obj, "finish", + deferred_locs, pass); + if (start_loc == UNKNOWN_LOCATION) + start_loc = caret_loc; + if (finish_loc == UNKNOWN_LOCATION) + finish_loc = caret_loc; + const char *label_str + = replayer->get_optional_string_attr (loc_obj, "label"); + range_label *label + = label_str ? new text_range_label (label_str) : NULL; + location_t range_loc = make_location (caret_loc, start_loc, finish_loc); + add_range (range_loc, SHOW_RANGE_WITH_CARET, label); + } + + json::array *fixits_arr + = replayer->get_optional_array_attr (diag_obj, "fixits"); + if (fixits_arr) + for (auto fixit_iter : *fixits_arr) + { + json::object *fixit_obj = dyn_cast (fixit_iter); + if (!fixit_obj) + replayer->fatal_error (fixit_iter, + "expected an object within the %qs array", + "fixits"); + on_fixit_obj (replayer, fixit_obj, deferred_locs, pass); + } + + json::array *path_arr + = replayer->get_optional_array_attr (diag_obj, "path"); + if (path_arr) + { + pretty_printer pp; + simple_diagnostic_path *path = new simple_diagnostic_path (&pp); + set_path (path); + for (auto event_iter : *path_arr) + { + json::object *event_obj = dyn_cast (event_iter); + if (!event_obj) + replayer->fatal_error (event_iter, + "expected an object within the %qs array", + "path"); + location_t loc + = replayer->get_optional_expanded_location_attr (event_obj, + "location", + deferred_locs, + pass); + const char *desc_str + = replayer->get_required_string_attr (event_obj, "description"); + tree fndecl = NULL_TREE; + if (const char *func_str + = replayer->get_optional_string_attr (event_obj, "function")) + fndecl = make_placeholder_fndecl (func_str); + long depth = replayer->get_required_long_attr (event_obj, "depth"); + path->add_event (loc, fndecl, depth, "%s", desc_str); + } + } + } + +private: + /* Compare with diagnostic-format-json.cc:json_from_fixit_hint. */ + void on_fixit_obj (json_replayer *replayer, + json::object *fixit_obj, + deferred_locations *deferred_locs, int pass) + { + location_t start_loc + = replayer->get_required_expanded_location_attr (fixit_obj, "start", + deferred_locs, pass); + location_t next_loc + = replayer->get_required_expanded_location_attr (fixit_obj, "next", + deferred_locs, pass); + const char *string + = replayer->get_required_string_attr (fixit_obj, "string"); + if (pass) + maybe_add_fixit (start_loc, next_loc, string); + } +}; + +class json_replayer_rule : public diagnostic_metadata::rule +{ +public: + json_replayer_rule (json_replayer *replayer, json::object *diag_obj) + : m_replayer (replayer), m_diag_obj (diag_obj) + {} + + char *make_description () const final override + { + if (const char *option + = m_replayer->get_optional_string_attr (m_diag_obj, "option")) + return xstrdup (option); + return NULL; + } + + char *make_url () const final override + { + if (const char *option_url + = m_replayer->get_optional_string_attr (m_diag_obj, "option_url")) + return xstrdup (option_url); + return NULL; + } + +private: + json_replayer *m_replayer; + json::object *m_diag_obj; +}; + + +/* Replay the diagnotic DIAG_OBJ to global_dc. + In pass 0, do everything except actually replay the diagnostic, + deferring the creation of location_t values. + In pass 1, actually emit the diagostic, using real location_t values. + Exit with an error if DIAG_OBJ does not match the expected output format. */ + +void +json_replayer::emit_diag_obj (json::object *diag_obj, + deferred_locations *deferred_locs, + int pass) +{ + auto_diagnostic_group d; + + diagnostic_t kind = get_diagnostic_kind (diag_obj); + const char *message_str = get_required_string_attr (diag_obj, "message"); + + json_rich_location rich_loc (this, diag_obj, deferred_locs, pass); + + diagnostic_metadata meta; + + if (json::object *metadata_obj = get_optional_object_attr (diag_obj, + "metadata")) + { + long cwe_long; + if (get_optional_long_attr (metadata_obj, "cwe", &cwe_long)) + meta.add_cwe (cwe_long); + } + + bool emitted = true; + if (pass == 1) + { + const int option = 0; + json_replayer_rule rule (this, diag_obj); + meta.add_rule (rule); + emitted = emit_diagnostic (kind, &rich_loc, &meta, option, + "%s", message_str); + } + + if (emitted) + if (json::value *children_val = diag_obj->get ("children")) + { + json::array *children_arr = dyn_cast (children_val); + if (!children_arr) + fatal_error (children_val, + "expected an array for the value of %qs", + "children"); + for (auto child_val : *children_arr) + { + /* We expect an array of objects. */ + json::object *child_obj = dyn_cast (child_val); + if (!child_obj) + fatal_error (child_val, + "expected an object within the %qs array", + "children"); + emit_diag_obj (child_obj, deferred_locs, pass); + } + } +} + +/* Replay the diagnotics in JV to global_dc. + Exit with an error if JV does not match the expected output format. */ + +void +json_replayer::emit_json_as_diagnostics (json::value *jv) +{ + /* We expect an array as the top-level value. */ + json::array *toplev_arr = dyn_cast (jv); + if (!toplev_arr) + fatal_error (jv, "expected an array as the top-level value"); + + deferred_locations deferred_locs; + + for (int pass = 0; pass < 2; pass++) + { + for (auto diag_val : *toplev_arr) + { + /* We expect an object. */ + json::object *diag_obj = require_object (diag_val); + emit_diag_obj (diag_obj, &deferred_locs, pass); + } + if (pass == 0) + deferred_locs.generate_location_t_values (); + } +} + +/* Attempt to load a json file from FILENAME and replay it. + Exit on any errors. */ + +void +replay_json (const char *filename) +{ + json_replayer *p = new json_replayer (filename); + json_replayer_diagnostic_client_data_hooks *hooks + = new json_replayer_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) + { + hooks->stash (jv); + p->emit_json_as_diagnostics (jv); + } +} diff --git a/gcc/json/json-replay.h b/gcc/json/json-replay.h new file mode 100644 index 00000000000..f2776aa4171 --- /dev/null +++ b/gcc/json/json-replay.h @@ -0,0 +1,26 @@ +/* Re-emitting diagnostics saved in JSON 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_JSON_JSON_REPLAY_H +#define GCC_JSON_JSON_REPLAY_H + +extern void replay_json (const char *filename); + +#endif /* GCC_JSON_JSON_H */ diff --git a/gcc/json/lang-specs.h b/gcc/json/lang-specs.h new file mode 100644 index 00000000000..da9a4f25757 --- /dev/null +++ b/gcc/json/lang-specs.h @@ -0,0 +1,26 @@ +/* lang-specs.h -- gcc driver specs for the JSON "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 json "frontend". */ + +{".json", "@json", 0, 1, 0}, +/* FIXME: */ +{"@json", "json-replay %i %(cc1_options)", + 0, 1, 0}, diff --git a/gcc/json/lang.opt b/gcc/json/lang.opt new file mode 100644 index 00000000000..4c75f5d35dd --- /dev/null +++ b/gcc/json/lang.opt @@ -0,0 +1,31 @@ +; Options for the JSON 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 +JSON + +fallow-comments +JSON Var(flag_allow_comments) +Extend JSON to support comments + +; This comment is to ensure we retain the blank line above. diff --git a/gcc/testsuite/json/invalid-json-array-missing-comma.json b/gcc/testsuite/json/invalid-json-array-missing-comma.json new file mode 100644 index 00000000000..0f32d38420e --- /dev/null +++ b/gcc/testsuite/json/invalid-json-array-missing-comma.json @@ -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/json/invalid-json-array-with-trailing-comma.json b/gcc/testsuite/json/invalid-json-array-with-trailing-comma.json new file mode 100644 index 00000000000..05b74a81efc --- /dev/null +++ b/gcc/testsuite/json/invalid-json-array-with-trailing-comma.json @@ -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/json/invalid-json-bad-token.json b/gcc/testsuite/json/invalid-json-bad-token.json new file mode 100644 index 00000000000..7756eef1add --- /dev/null +++ b/gcc/testsuite/json/invalid-json-bad-token.json @@ -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/json/invalid-json-object-missing-comma.json b/gcc/testsuite/json/invalid-json-object-missing-comma.json new file mode 100644 index 00000000000..9d2bf9476b1 --- /dev/null +++ b/gcc/testsuite/json/invalid-json-object-missing-comma.json @@ -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/json/invalid-json-object-with-trailing-comma.json b/gcc/testsuite/json/invalid-json-object-with-trailing-comma.json new file mode 100644 index 00000000000..e1aae9b350c --- /dev/null +++ b/gcc/testsuite/json/invalid-json-object-with-trailing-comma.json @@ -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/json/invalid-jsondump-diag-not-an-object.json b/gcc/testsuite/json/invalid-jsondump-diag-not-an-object.json new file mode 100644 index 00000000000..6f5d37f08e1 --- /dev/null +++ b/gcc/testsuite/json/invalid-jsondump-diag-not-an-object.json @@ -0,0 +1,6 @@ +[ 42 ] /* { dg-error "expected an object" } */ + +/* { dg-begin-multiline-output "" } + 1 | [ 42 ] + | ^~ + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/json/invalid-jsondump-kind-not-a-string.json b/gcc/testsuite/json/invalid-jsondump-kind-not-a-string.json new file mode 100644 index 00000000000..ab55f1e5573 --- /dev/null +++ b/gcc/testsuite/json/invalid-jsondump-kind-not-a-string.json @@ -0,0 +1,20 @@ +[ + { + "kind": 42, /* { dg-error "expected the value of 'kind' to be a string" } */ + "locations": [], + "column-origin": 1, + "option": "-Wanalyzer-unsafe-call-within-signal-handler", + "escape-source": false, + "children": [], + "option_url": "https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html#index-Wanalyzer-unsafe-call-within-signal-handler", + "message": "call to \u2018fprintf\u2019 from within signal handler", + "metadata": { + "cwe": 479 + } + } +] + +/* { dg-begin-multiline-output "" } + 3 | "kind": 42, + | ^~ + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/json/invalid-jsondump-not-an-array.json b/gcc/testsuite/json/invalid-jsondump-not-an-array.json new file mode 100644 index 00000000000..9b14ea35565 --- /dev/null +++ b/gcc/testsuite/json/invalid-jsondump-not-an-array.json @@ -0,0 +1,6 @@ +{ "foo": "bar" } /* { dg-error "expected an array as the top-level value" } */ + +/* { dg-begin-multiline-output "" } + 1 | { "foo": "bar" } + | ^~~~~~~~~~~~~~~~ + { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/json/json.exp b/gcc/testsuite/json/json.exp new file mode 100644 index 00000000000..0a33ba53fb6 --- /dev/null +++ b/gcc/testsuite/json/json.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 json-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_JSON_FLAGS +if ![info exists DEFAULT_JSON_FLAGS] then { + set DEFAULT_JSON_FLAGS "-fallow-comments" +} +# Initialize `dg'. +dg-init + +dg-runtest [lsort \ + [glob -nocomplain $srcdir/$subdir/*.json ] ] "" $DEFAULT_JSON_FLAGS + +# All done. +dg-finish diff --git a/gcc/testsuite/json/signal-1.c.json b/gcc/testsuite/json/signal-1.c.json new file mode 100644 index 00000000000..5f4962209a9 --- /dev/null +++ b/gcc/testsuite/json/signal-1.c.json @@ -0,0 +1,131 @@ +[ + { + "kind": "warning", + "locations": [ + { + "finish": { + "byte-column": 33, + "display-column": 33, + "line": 13, + "file": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c", + "column": 33 + }, + "caret": { + "byte-column": 3, + "display-column": 3, + "line": 13, + "file": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c", + "column": 3 + } + } + ], + "path": [ + { + "location": { + "byte-column": 5, + "display-column": 5, + "line": 21, + "file": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c", + "column": 5 + }, + "description": "entry to \u2018main\u2019", + "depth": 1, + "function": "main" + }, + { + "location": { + "byte-column": 3, + "display-column": 3, + "line": 25, + "file": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c", + "column": 3 + }, + "description": "registering \u2018handler\u2019 as signal handler", + "depth": 1, + "function": "main" + }, + { + "description": "later on, when the signal is delivered to the process", + "depth": 0 + }, + { + "location": { + "byte-column": 13, + "display-column": 13, + "line": 16, + "file": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c", + "column": 13 + }, + "description": "entry to \u2018handler\u2019", + "depth": 1, + "function": "handler" + }, + { + "location": { + "byte-column": 3, + "display-column": 3, + "line": 18, + "file": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c", + "column": 3 + }, + "description": "calling \u2018custom_logger\u2019 from \u2018handler\u2019", + "depth": 1, + "function": "handler" + }, + { + "location": { + "byte-column": 6, + "display-column": 6, + "line": 11, + "file": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c", + "column": 6 + }, + "description": "entry to \u2018custom_logger\u2019", + "depth": 2, + "function": "custom_logger" + }, + { + "location": { + "byte-column": 3, + "display-column": 3, + "line": 13, + "file": "../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c", + "column": 3 + }, + "description": "call to \u2018fprintf\u2019 from within signal handler", + "depth": 2, + "function": "custom_logger" + } + ], + "column-origin": 1, + "option": "-Wanalyzer-unsafe-call-within-signal-handler", + "escape-source": false, + "children": [], + "option_url": "https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html#index-Wanalyzer-unsafe-call-within-signal-handler", + "message": "call to \u2018fprintf\u2019 from within signal handler", + "metadata": { + "cwe": 479 + } + } +] + +/* { dg-begin-multiline-output "" } +../../src/gcc/testsuite/gcc.dg/analyzer/signal-1.c:13:3: warning: call to ‘fprintf’ from within signal handler [CWE-479] [-Wanalyzer-unsafe-call-within-signal-handler] + 'main': events 1-2 + | + |...... + | + event 3 + | + |json-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: various things wrong here diff --git a/gcc/testsuite/lib/json-dg.exp b/gcc/testsuite/lib/json-dg.exp new file mode 100644 index 00000000000..18701f84cac --- /dev/null +++ b/gcc/testsuite/lib/json-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 json callbacks for dg.exp. + +proc json-dg-test { prog do_what extra_tool_flags } { + set result \ + [gcc-dg-test-1 json_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. + # + # json 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. + # json 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 json-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 json-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 json-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/json.exp b/gcc/testsuite/lib/json.exp new file mode 100644 index 00000000000..52ba75a1a14 --- /dev/null +++ b/gcc/testsuite/lib/json.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. + +# +# json 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 json_target_compile { source dest type options } { + set return_val [target_compile $source $dest $type $options] + return $return_val; +} -- 2.26.3