From patchwork Tue Nov 21 22:20:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 168007 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:2b07:b0:403:3b70:6f57 with SMTP id io7csp950266vqb; Tue, 21 Nov 2023 14:22:16 -0800 (PST) X-Google-Smtp-Source: AGHT+IHrw9/rBZBMeqz6g1GLQzxUo3FMc6cfq4mg2Ck2bqO14cRMZIrh/mZUkc8fNwRf9gX9UTKs X-Received: by 2002:a05:622a:58a:b0:41e:3754:a49c with SMTP id c10-20020a05622a058a00b0041e3754a49cmr521000qtb.64.1700605335818; Tue, 21 Nov 2023 14:22:15 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1700605335; cv=pass; d=google.com; s=arc-20160816; b=H5nNqVItUbn2uCXCRbzamVg4WuYt47fS3RzhlAKY9ROIygEr61ClOaLlWNurBCmBSR ISuhScL8zmC1w3i3UDrIx3D8qNvzVhUdYymcQ2bbdCOvoOZQokufhTYiMGu3SNbh0e/g vaITFENjzQ6bA/sUzw+Bmnydtl04XENJ33xhoJZ7o1hs9PsiXpL5Z1WomM1LthY+eYSA epfGk/Z9QJu5gNozeU/0lDFHXmriKdp50rrw5pVCMmDyKoxxYTk00g5qieAq3ORGTwN3 xYQyoFG756Fed6MhD6Z4cahvB8TCVOTITlqlQjmpKig7R6jmwrGQyGiQ4AMoy5QJZsit x1fw== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature:arc-filter:dmarc-filter:delivered-to; bh=qOt1Nd7SjsAqwWOrGY9L4yWuEaTMyJthGFd+WfA87sI=; fh=rfR2SIXf27jLXVYT4RHMSVZgLIsrp2E1IHKtUM1Ws70=; b=uynHeH9xpiDmhzGlUNWwRUu5+nEbPOPEX1+nRXnQINgBsD5QpT3EQVLkvoowlnIPNF axDdQFrTeyvjbcXhNk9MsR1arT/SFVRKyr1Pm6jSuKk5aIfbt6RnVsTn445VtP29iIe7 EgRGlOJ8K/bNk+xmqiwJTcKQFcZV45b/yPpbnF1v1aYdxNi8AuHhZg2V8ql3EhzdQ2LC 3qlokqNoQ4aAYVgMq+yVdWcK1ph2MRhYjdSLx4NKfDBgbdpkb5LPav7oFvaRxejtr09w HFGtcOEkq38mFi/+s4tvt8t9iDDW2RGTvJ9N353nWL6Yu76uxyPvyTL+gwqOpLmFwvep 5siw== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=KMYQTOcz; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (server2.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id fb27-20020a05622a481b00b004237035fbd7si1982463qtb.358.2023.11.21.14.22.15 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Nov 2023 14:22:15 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=KMYQTOcz; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 5D555386C5B8 for ; Tue, 21 Nov 2023 22:21:35 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org 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 6B7A73858D39 for ; Tue, 21 Nov 2023 22:20:24 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 6B7A73858D39 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 6B7A73858D39 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700605230; cv=none; b=hzw10HF3gq+4VPDMAEUV98IKLBsv1ccCb9nQb15XcSO3Gi63G21A6ABtM4vKBOeJMRmrd0hAD+KbBbTixTBsrH5RVcbP6Nm8SuGW/ytp0m9KkztBtSi/l90c2FmFyPGyz6z3HiWkLDInEHePlT9bnDLlD0RyeqsNvR7q+e6isWk= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700605230; c=relaxed/simple; bh=BvXpNM4Lm11pB/6+MmCinus92Ku17AATeLgYqbi6KLQ=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=GjqeO46hY3BjDYaFlsy/kpWaZIC/vwlvTfta0YOv6rJNZn1ds4h4xAtNv5oHMvWn9d7VuMx/EJG9J6hUO7sQ7HisRwJZgN0VvopW06SR/egSudtx0dxRK0z5SyTeObJNVItV6Pw/Ry/Xu7rWjSqrpNWrlvo+lX8k9zrwr5Ck3YA= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700605224; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=qOt1Nd7SjsAqwWOrGY9L4yWuEaTMyJthGFd+WfA87sI=; b=KMYQTOczQAxRFJQ+tYrhJIlmlLTjMzmLGcNBN8OZJ7gjmq1C2qkmWIJdXsEeVp2SHAu06/ h4VUtiLXu0gY8vApDK1dKgUfAgjvh9ZYsQNb5ZkcrMwdFLo9zHJcYbg5X4tpUwUTyYotq1 mDhtWFOBL5uaX2OBSi/Xaz0TTxamlbk= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-556-IX7RH60TNcWpO3x5vuZIHw-1; Tue, 21 Nov 2023 17:20:22 -0500 X-MC-Unique: IX7RH60TNcWpO3x5vuZIHw-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id B2911821C24; Tue, 21 Nov 2023 22:20:21 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.22.10.115]) by smtp.corp.redhat.com (Postfix) with ESMTP id 55BB8492BFA; Tue, 21 Nov 2023 22:20:21 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org, binutils@sourceware.org Cc: Nick Clifton , Simon Sobisch , David Malcolm Subject: [PATCH 1/5] libdiagnostics v2: header and examples Date: Tue, 21 Nov 2023 17:20:14 -0500 Message-Id: <20231121222019.646253-2-dmalcolm@redhat.com> In-Reply-To: <20231121222019.646253-1-dmalcolm@redhat.com> References: <20231106222959.2707741-1-dmalcolm@redhat.com> <20231121222019.646253-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.3 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_LOTSOFHASH, KAM_SHORT, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1783213940745645868 X-GMAIL-MSGID: 1783213940745645868 Changed in v2: * Changed from diagnostic_location_t -> const diagnostic_physical_location * * Add entrypoint: diagnostic_finish_va * add new type diagnostic_text_sink, and new entrypoints for enabling/disabling options on it * add new debugging entrypoints for dumping objects to a FILE * * new test cases Blurb from v1: Here's a work-in-progress patch for GCC that adds a libdiagnostics.h header describing the public interface, along with various testcases that show usage examples for the API. Various aspects of this need work; posting now for early feedback on overall direction. How does the interface look? gcc/ChangeLog: * libdiagnostics.h: New file. gcc/testsuite/ChangeLog: * libdiagnostics.dg/test-error-with-note.c: New test. * libdiagnostics.dg/test-error.c: New test. * libdiagnostics.dg/test-fix-it-hint.c: New test. * libdiagnostics.dg/test-helpers.h: New. * libdiagnostics.dg/test-labelled-ranges.c: New test. * libdiagnostics.dg/test-logical-location.c: New test. * libdiagnostics.dg/test-metadata.c: New test. * libdiagnostics.dg/test-multiple-lines.c: New test. * libdiagnostics.dg/test-note-with-fix-it-hint.c: New test. * libdiagnostics.dg/test-warning.c: New test. * libdiagnostics.dg/test-write-sarif-to-file.c: New test. * libdiagnostics.dg/test-write-text-to-file.c: New test. --- gcc/libdiagnostics.h | 602 ++++++++++++++++++ gcc/testsuite/libdiagnostics.dg/test-dump.c | 55 ++ .../libdiagnostics.dg/test-error-with-note.c | 57 ++ gcc/testsuite/libdiagnostics.dg/test-error.c | 49 ++ .../libdiagnostics.dg/test-fix-it-hint.c | 49 ++ .../libdiagnostics.dg/test-helpers.h | 29 + .../libdiagnostics.dg/test-labelled-ranges.c | 52 ++ .../libdiagnostics.dg/test-logical-location.c | 60 ++ .../libdiagnostics.dg/test-metadata.c | 54 ++ .../libdiagnostics.dg/test-multiple-lines.c | 61 ++ .../libdiagnostics.dg/test-no-column.c | 41 ++ .../test-note-with-fix-it-hint.c | 52 ++ .../test-text-sink-options.c | 46 ++ .../libdiagnostics.dg/test-warning.c | 52 ++ .../test-write-sarif-to-file.c | 46 ++ .../test-write-text-to-file.c | 47 ++ 16 files changed, 1352 insertions(+) create mode 100644 gcc/libdiagnostics.h create mode 100644 gcc/testsuite/libdiagnostics.dg/test-dump.c create mode 100644 gcc/testsuite/libdiagnostics.dg/test-error-with-note.c create mode 100644 gcc/testsuite/libdiagnostics.dg/test-error.c create mode 100644 gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.c create mode 100644 gcc/testsuite/libdiagnostics.dg/test-helpers.h create mode 100644 gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.c create mode 100644 gcc/testsuite/libdiagnostics.dg/test-logical-location.c create mode 100644 gcc/testsuite/libdiagnostics.dg/test-metadata.c create mode 100644 gcc/testsuite/libdiagnostics.dg/test-multiple-lines.c create mode 100644 gcc/testsuite/libdiagnostics.dg/test-no-column.c create mode 100644 gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint.c create mode 100644 gcc/testsuite/libdiagnostics.dg/test-text-sink-options.c create mode 100644 gcc/testsuite/libdiagnostics.dg/test-warning.c create mode 100644 gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file.c create mode 100644 gcc/testsuite/libdiagnostics.dg/test-write-text-to-file.c diff --git a/gcc/libdiagnostics.h b/gcc/libdiagnostics.h new file mode 100644 index 000000000000..5c4e203b6a0a --- /dev/null +++ b/gcc/libdiagnostics.h @@ -0,0 +1,602 @@ +/* A pure C API for emitting diagnostics. + Copyright (C) 2023 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 +. */ + +#ifndef LIBDIAGNOSTICS_H +#define LIBDIAGNOSTICS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/********************************************************************** + Macros for attributes. + These are all currently empty, and thus for the human reader rather than + the compiler. + **********************************************************************/ + +#define LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL(ARG_NUM) + +#define LIBDIAGNOSTICS_PARAM_CAN_BE_NULL(ARG_NUM) + +#define LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING(FMT_ARG_NUM, ARGS_ARG_NUM) + + +/********************************************************************** + Data structures and types. + All structs within the API are opaque. + **********************************************************************/ + +/* An opaque bundle of state for a client of the library. + Has zero of more "sinks" to which diagnostics are emitted. + Responsibilities: + - location-management + - caching of source file content + - patch generation. */ +typedef struct diagnostic_manager diagnostic_manager; + +/* Types relating to diagnostic output sinks. */ + +typedef struct diagnostic_text_sink diagnostic_text_sink; + +/* An enum for determining if we should colorize a text output sink. */ +enum diagnostic_colorize +{ + DIAGNOSTIC_COLORIZE_IF_TTY, + DIAGNOSTIC_COLORIZE_NO, + DIAGNOSTIC_COLORIZE_YES +}; + +/* An enum for choosing the SARIF version for a SARIF output sink. + Eventually the SARIF output may support multiple SARIF versions. */ + +enum diagnostic_sarif_version +{ + DIAGNOSTIC_SARIF_VERSION_2_1_0 +}; + +/* Types relating to "physical" source locations i.e. locations within + specific files expressed via line/column. */ + +/* Opaque type describing a particular input file. */ +typedef struct diagnostic_file diagnostic_file; + +/* Opaque type representing a key into a database of source locations within + a diagnostic_manager. Locations are created by various API calls into + the diagnostic_manager expressing source code points and ranges. They + persist until the diagnostic_manager is released, which cleans them + up. + + NULL means "UNKNOWN", and can be returned by the manager as a + fallback when a problem occurs (e.g. too many locations). + + A diagnostic_location can be a single point within the source code, + such as here (at the the '"' at the start of the string literal): + + int i = "foo"; + ^ + + or be a range with a start and finish, and a "caret" location. + + a = (foo && bar) + ~~~~~^~~~~~~ + where the caret here is at the first "&", and the start and finish + are at the parentheses. */ + +typedef struct diagnostic_physical_location diagnostic_physical_location; + +/* Types for storing line and column information in text files. + + Both libdiagnostics and emacs number source *lines* starting at 1, but + they have differing conventions for *columns*. + + libdiagnostics uses a 1-based convention for source columns, + whereas Emacs's M-x column-number-mode uses a 0-based convention. + + For example, an error in the initial, left-hand + column of source line 3 is reported by libdiagnostics as: + + some-file.c:3:1: error: ...etc... + + On navigating to the location of that error in Emacs + (e.g. via "next-error"), + the locus is reported in the Mode Line + (assuming M-x column-number-mode) as: + + some-file.c 10% (3, 0) + + i.e. "3:1:" in libdiagnostics corresponds to "(3, 0)" in Emacs. */ + +typedef unsigned int diagnostic_line_num_t; +typedef unsigned int diagnostic_column_num_t; + +/* An opaque type describing a "logical" source location + e.g. "within function 'foo'". */ + +typedef struct diagnostic_logical_location diagnostic_logical_location; + +/* An enum for discriminating between different kinds of logical location + for a diagnostic. + + Roughly corresponds to logicalLocation's "kind" property in SARIF v2.1.0 + (section 3.33.7). */ + +enum diagnostic_logical_location_kind_t +{ + DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE +}; + +/* A "diagnostic" is an opaque bundle of state for a particular + diagnostic that is being constructed in memory. + + A diagnostic has a primary location and zero or more secondary + locations. For example: + + a = (foo && bar) + ~~~~~^~~~~~~ + + This diagnostic has a single diagnostic_location, with the caret + at the first "&", and the start/finish at the parentheses. + + Contrast with: + + a = (foo && bar) + ~~~ ^~ ~~~ + + This diagnostic has three locations + - The primary location (at "&&") has its caret and start location at + the first "&" and end at the second "&. + - The secondary location for "foo" has its start and finish at the "f" + and "o" of "foo"; the caret is not flagged for display, but is perhaps at + the "f" of "foo". + - Similarly, the other secondary location (for "bar") has its start and + finish at the "b" and "r" of "bar"; the caret is not flagged for + display, but is perhaps at the"b" of "bar". */ +typedef struct diagnostic diagnostic; + +enum diagnostic_level +{ + DIAGNOSTIC_LEVEL_ERROR, + DIAGNOSTIC_LEVEL_WARNING, + DIAGNOSTIC_LEVEL_NOTE +}; + +/********************************************************************** + API entrypoints. + **********************************************************************/ + +/* Create a new diagnostic_manager. + The client needs to call diagnostic_release_manager on it at some + point. + Note that no output sinks are created by default. */ + +extern diagnostic_manager * +diagnostic_manager_new (void); + +/* Release a diagnostic_manager. + This will flush output to all of the output sinks, and clean up. */ + +extern void +diagnostic_manager_release (diagnostic_manager *) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +/* Optional metadata about the manager. */ + +/* Set a string suitable for use as the value of the SARIF "name" property + (SARIF v2.1.0 section 3.19.8). */ + +extern void +diagnostic_manager_set_tool_name (diagnostic_manager *diag_mgr, + const char *value) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Set a string suitable for use as the value of the SARIF "fullName" property + (SARIF v2.1.0 section 3.19.9). */ + +extern void +diagnostic_manager_set_full_name (diagnostic_manager *diag_mgr, + const char *value) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Set a string suitable for use as the value of the SARIF "version" property + (SARIF v2.1.0 section 3.19.13). */ + +extern void +diagnostic_manager_set_version_string (diagnostic_manager *diag_mgr, + const char *value) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Set a string suitable for use as the value of the SARIF "informationUri" + property (SARIF v2.1.0 section 3.19.17). */ + +extern void +diagnostic_manager_set_version_url (diagnostic_manager *diag_mgr, + const char *value) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Destinations for diagnostics. */ + +/* Add a new output sink to DIAG_MGR, which writes GCC-style diagnostics + to DST_STREAM. + Return a borrowed pointer to the sink, which is cleaned up when DIAG_MGR + is released. + The output for each diagnostic is written and flushed as each + diagnostic is finished. */ + +extern diagnostic_text_sink * +diagnostic_manager_add_text_sink (diagnostic_manager *diag_mgr, + FILE *dst_stream, + enum diagnostic_colorize colorize) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Functions to manipulate text sinks. */ + +/* Enable/disable printing of source text in the text sink. + Default: enabled. */ + +extern void +diagnostic_text_sink_set_source_printing_enabled (diagnostic_text_sink *text_sink, + int value) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +/* Enable/disable colorization of the characters of source text + that are underlined. + This should be true for clients that generate range information + (so that the ranges of code are colorized), + and false for clients that merely specify points within the + source code (to avoid e.g. colorizing just the first character in + a token, which would look strange). + Default: enabled. */ + +extern void +diagnostic_text_sink_set_labelled_source_colorization_enabled (diagnostic_text_sink *text_sink, + int value) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +/* Add a new output sink to DIAG_MGR, which writes SARIF of the given + version to DST_STREAM. + The output is not written until DIAG_MGR is released. */ + +extern void +diagnostic_manager_add_sarif_sink (diagnostic_manager *diag_mgr, + FILE *dst_stream, + enum diagnostic_sarif_version version) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Write a patch to DST_STREAM consisting of all fix-it hints + on all diagnostics that have been finished on DIAG_MGR. */ + +extern void +diagnostic_manager_write_patch (diagnostic_manager *diag_mgr, + FILE *dst_stream) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Location management. */ + +/* Create a new diagnostic_file * for file NAME. + + Repeated calls with matching NAMEs will return the + same object. + + If SARIF_SOURCE_LANGUAGE is non-NULL, it specifies a "sourceLanguage" + value for the file when use when writing SARIF. + See SARIF v2.1.0 Appendix J for suggested values for various + programmming languages. */ + +extern const diagnostic_file * +diagnostic_manager_new_file (diagnostic_manager *diag_mgr, + const char *name, + const char *sarif_source_language) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (3); + +// TODO +extern void +diagnostic_manager_debug_dump_file (diagnostic_manager *diag_mgr, + const diagnostic_file *file, + FILE *out) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3); + +/* Attempt to create a diagnostic_location representing + FILENAME:LINE_NUM, with no column information + (thus "the whole line"). */ + +extern const diagnostic_physical_location * +diagnostic_manager_new_location_from_file_and_line (diagnostic_manager *diag_mgr, + const diagnostic_file *file, + diagnostic_line_num_t line_num) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Attempt to create a diagnostic_physical_location representing + FILENAME:LINE_NUM:COLUMN_NUM. */ + +extern const diagnostic_physical_location * +diagnostic_manager_new_location_from_file_line_column (diagnostic_manager *diag_mgr, + const diagnostic_file *file, + diagnostic_line_num_t line_num, + diagnostic_column_num_t column_num) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2); + +/* Attempt to create a diagnostic_physical_location representing a + range within a source file, with a highlighted "caret" location. + + All must be within the same file, but they can be on different lines. + + For example, consider the location of the binary expression below: + + ...|__________1111111112222222 + ...|12345678901234567890123456 + ...| + 521|int sum (int foo, int bar) + 522|{ + 523| return foo + bar; + ...| ~~~~^~~~~ + 524|} + + The location's caret is at the "+", line 523 column 15, but starts + earlier, at the "f" of "foo" at column 11. The finish is at the "r" + of "bar" at column 19. */ + +extern const diagnostic_physical_location * +diagnostic_manager_new_location_from_range (diagnostic_manager *diag_mgr, + const diagnostic_physical_location *loc_caret, + const diagnostic_physical_location *loc_start, + const diagnostic_physical_location *loc_end) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (3) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (4); + +// TODO +extern void +diagnostic_manager_debug_dump_location (const diagnostic_manager *diag_mgr, + const diagnostic_physical_location *loc, + FILE *out) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3); + +/* A bundle of state describing a logical location in the user's source, + such as "in function 'foo'". + + SHORT_NAME can be NULL, or else a string suitable for use by + the SARIF logicalLocation "name" property (SARIF v2.1.0 section 3.33.4). + + FULLY_QUALIFIED_NAME can be NULL or else a string suitable for use by + the SARIF logicalLocation "fullyQualifiedName" property + (SARIF v2.1.0 section 3.33.5). + + DECORATED_NAME can be NULL or else a string suitable for use by + the SARIF logicalLocation "decoratedName" property + (SARIF v2.1.0 section 3.33.6). */ + +extern const diagnostic_logical_location * +diagnostic_manager_new_logical_location (diagnostic_manager *diag_mgr, + enum diagnostic_logical_location_kind_t kind, + const diagnostic_logical_location *parent, + const char *short_name, + const char *fully_qualified_name, + const char *decorated_name) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (3) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (4) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (5) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (6); + +// TODO +extern void +diagnostic_manager_debug_dump_logical_location (const diagnostic_manager *diag_mgr, + const diagnostic_logical_location *loc, + FILE *out) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3); + +/* Diagnostic groups. */ + +// FIXME: docs + +extern void +diagnostic_manager_begin_group (diagnostic_manager *diag_mgr) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +extern void +diagnostic_manager_end_group (diagnostic_manager *diag_mgr) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +/* Step-by-step creation of a diagnostic. */ + +extern diagnostic * +diagnostic_begin (diagnostic_manager *diag_mgr, + enum diagnostic_level level) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +#if 0 +// FIXME: TODO +extern void +diagnostic_set_option (diagnostic *diag, + const char *option, + const char *url); +#endif + +/* Associate this diagnostic with the given ID within + the Common Weakness Enumeration. */ + +extern void +diagnostic_set_cwe (diagnostic *diag, + unsigned cwe_id) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +/* Associate this diagnostic with a particular rule that has been violated + (such as in a coding standard, or within a specification). + The rule must have at least one of a title and a URL, but these + can be NULL. + A diagnostic can be associated with zero or more rules. */ + +extern void +diagnostic_add_rule (diagnostic *diag, + const char *title, + const char *url) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (3); + +/* Set the primary location of DIAG. */ + +extern void +diagnostic_set_location (diagnostic *diag, + const diagnostic_physical_location * loc) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2); + +/* Set the primary location of DIAG, with a label. */ + +extern void +diagnostic_set_location_with_label (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *fmt, ...) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3); + +/* Add a secondary location to DIAG. */ + +extern void +diagnostic_add_location (diagnostic *diag, + const diagnostic_physical_location * loc) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + +/* Add a secondary location to DIAG, with a label. */ + +extern void +diagnostic_add_location_with_label (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *text) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3); + +/* Set the logical location of DIAG. */ + +extern void +diagnostic_set_logical_location (diagnostic *diag, + const diagnostic_logical_location *logical_loc) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2); + +/* Fix-it hints. */ + +extern void +diagnostic_add_fix_it_hint_insert_before (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *addition) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3); + +extern void +diagnostic_add_fix_it_hint_insert_after (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *addition) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3); + +extern void +diagnostic_add_fix_it_hint_replace (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *replacement) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (3); + +extern void +diagnostic_add_fix_it_hint_delete (diagnostic *diag, + const diagnostic_physical_location *loc) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_CAN_BE_NULL (2); + +/* Emit DIAG to all sinks of its manager, and release DIAG. + Use FMT for the message. + TODO: this uses gcc's pretty-print format, which is *not* printf. + TODO: who is responsible for putting FMT through gettext? */ + +extern void +diagnostic_finish (diagnostic *diag, const char *fmt, ...) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 3); + +// FIXME + +extern void +diagnostic_finish_va (diagnostic *diag, const char *fmt, va_list *args) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) + LIBDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2) + LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 0); + +// FIXME: we might want one that handles plurals +#if 0 +extern void +diagnostic_finish_n (diagnostic_manager *diag_mgr, + int n, + const char *singular_fmt, + const char *plural_fmt, + ...); +#endif + +/* TODO: + + DEFERRED: + - thread-safety + - plural forms + - enum about what a "column number" means (bytes, unichars, etc) + - locations within binary files + - options and URLs for warnings + - enable/disable of warnings by kind + - execution paths associated with/triggering a problem + - plugin metadata. */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LIBDIAGNOSTICS_H */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-dump.c b/gcc/testsuite/libdiagnostics.dg/test-dump.c new file mode 100644 index 000000000000..c9a7d61315cb --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-dump.c @@ -0,0 +1,55 @@ +/* Usage example of dump API. */ + +#include "libdiagnostics.h" + +const int line_num = 42; + +int +main () +{ + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, "foo.c", "c"); + + fprintf (stderr, "file: "); + diagnostic_manager_debug_dump_file (diag_mgr, file, stderr); + fprintf (stderr, "\n"); + + const diagnostic_physical_location *loc_start + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 8); + const diagnostic_physical_location *loc_end + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 19); + const diagnostic_physical_location *loc_range + = diagnostic_manager_new_location_from_range (diag_mgr, + loc_start, + loc_start, + loc_end); + + fprintf (stderr, "loc_start: "); + diagnostic_manager_debug_dump_location (diag_mgr, loc_start, stderr); + fprintf (stderr, "\n"); + + fprintf (stderr, "loc_end: "); + diagnostic_manager_debug_dump_location (diag_mgr, loc_end, stderr); + fprintf (stderr, "\n"); + + fprintf (stderr, "loc_range: "); + diagnostic_manager_debug_dump_location (diag_mgr, loc_range, stderr); + fprintf (stderr, "\n"); + + + const diagnostic_logical_location *logical_loc + = diagnostic_manager_new_logical_location (diag_mgr, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION, + NULL, /* parent */ + "test_short_name", + "test_qualified_name", + "test_decorated_name"); + + fprintf (stderr, "logical_loc: "); + diagnostic_manager_debug_dump_logical_location (diag_mgr, logical_loc, stderr); + fprintf (stderr, "\n"); + + diagnostic_manager_release (diag_mgr); + return 0; +}; diff --git a/gcc/testsuite/libdiagnostics.dg/test-error-with-note.c b/gcc/testsuite/libdiagnostics.dg/test-error-with-note.c new file mode 100644 index 000000000000..0f4ee70232ca --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-error-with-note.c @@ -0,0 +1,57 @@ +/* Example of emitting an error with an associated note. + + Intended output is similar to: + +PATH/test-error-with-note.c:6: error: can't find 'foo' + 6 | PRINT "hello world!"; + | ^~~~~~~~~~~~ +PATH/test-error-with-note.c:6: note: have you looked behind the couch? + + along with the equivalent in SARIF. */ + +#include "libdiagnostics.h" + +/* +_________111111111122 +123456789012345678901 +PRINT "hello world!"; +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + diagnostic_manager_add_text_sink (diag_mgr, stderr, + DIAGNOSTIC_COLORIZE_IF_TTY); + diagnostic_manager_add_sarif_sink (diag_mgr, stderr, + DIAGNOSTIC_SARIF_VERSION_2_1_0); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c"); + const diagnostic_physical_location *loc_start + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 8); + const diagnostic_physical_location *loc_end + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 19); + const diagnostic_physical_location *loc_range + = diagnostic_manager_new_location_from_range (diag_mgr, + loc_start, + loc_start, + loc_end); + + diagnostic_manager_begin_group (diag_mgr); + + diagnostic *err = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_ERROR); + diagnostic_set_location (err, loc_range); + diagnostic_finish (err, "can't find %qs", "foo"); + + diagnostic *note = diagnostic_begin (diag_mgr, DIAGNOSTIC_LEVEL_NOTE); + diagnostic_set_location (note, loc_range); + diagnostic_finish (note, "have you looked behind the couch?"); + + diagnostic_manager_end_group (diag_mgr); + + diagnostic_manager_release (diag_mgr); + return 0; +}; diff --git a/gcc/testsuite/libdiagnostics.dg/test-error.c b/gcc/testsuite/libdiagnostics.dg/test-error.c new file mode 100644 index 000000000000..1b99c4e5299f --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-error.c @@ -0,0 +1,49 @@ +/* Example of emitting an error. + + Intended output is similar to: + +PATH/test-error-with-note.c:6: error: can't find 'foo' + 6 | PRINT "hello world!"; + | ^~~~~~~~~~~~ + + along with the equivalent in SARIF. */ + +#include "libdiagnostics.h" + +/* +_________111111111122 +123456789012345678901 +PRINT "hello world!"; +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + diagnostic_manager_add_text_sink (diag_mgr, stderr, + DIAGNOSTIC_COLORIZE_IF_TTY); + diagnostic_manager_add_sarif_sink (diag_mgr, stderr, + DIAGNOSTIC_SARIF_VERSION_2_1_0); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c"); + const diagnostic_physical_location *loc_start + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 8); + const diagnostic_physical_location *loc_end + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 19); + const diagnostic_physical_location *loc_range + = diagnostic_manager_new_location_from_range (diag_mgr, + loc_start, + loc_start, + loc_end); + + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_ERROR); + diagnostic_set_location (d, loc_range); + + diagnostic_finish (d, "can't find %qs", "foo"); + + diagnostic_manager_release (diag_mgr); + return 0; +}; diff --git a/gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.c b/gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.c new file mode 100644 index 000000000000..d3f7e82d1173 --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-fix-it-hint.c @@ -0,0 +1,49 @@ +/* Example of a fix-it hint, including patch generation. + + Intended output is similar to: + +PATH/test-fix-it-hint.c:19: error: unknown field 'colour'; did you mean 'color' + 19 | return p->colour; + | ^~~~~~ + | color + + along with the equivalent in SARIF, and a generated patch (on stderr) to + make the change. */ + +#include "libdiagnostics.h" +#include "test-helpers.h" + +/* +_________11111111112 +12345678901234567890 + return p->colour; +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + diagnostic_manager_add_text_sink (diag_mgr, stderr, + DIAGNOSTIC_COLORIZE_IF_TTY); + diagnostic_manager_add_sarif_sink (diag_mgr, stderr, + DIAGNOSTIC_SARIF_VERSION_2_1_0); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c"); + const diagnostic_physical_location *loc_token + = make_range (diag_mgr, file, line_num, 13, 18); + + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_ERROR); + diagnostic_set_location (d, loc_token); + + diagnostic_add_fix_it_hint_replace (d, loc_token, "color"); + + diagnostic_finish (d, "unknown field %qs; did you mean %qs", "colour", "color"); + + diagnostic_manager_write_patch (diag_mgr, stderr); + + diagnostic_manager_release (diag_mgr); + return 0; +} diff --git a/gcc/testsuite/libdiagnostics.dg/test-helpers.h b/gcc/testsuite/libdiagnostics.dg/test-helpers.h new file mode 100644 index 000000000000..ad22284fb495 --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-helpers.h @@ -0,0 +1,29 @@ +/* Common utility code shared between test cases. */ + +#ifndef TEST_HELPERS_H +#define TEST_HELPERS_H + +const diagnostic_physical_location * +make_range (diagnostic_manager *diag_mgr, + const diagnostic_file *file, + diagnostic_line_num_t line_num, + diagnostic_column_num_t start_column, + diagnostic_column_num_t end_column) +{ + const diagnostic_physical_location *loc_start + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, + file, + line_num, + start_column); + const diagnostic_physical_location *loc_end + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, + file, + line_num, + end_column); + return diagnostic_manager_new_location_from_range (diag_mgr, + loc_start, + loc_start, + loc_end); +} + +#endif /* #ifndef TEST_HELPERS_H */ diff --git a/gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.c b/gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.c new file mode 100644 index 000000000000..b5723810b1fc --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-labelled-ranges.c @@ -0,0 +1,52 @@ +/* Example of multiple locations, with labelling of ranges. + + Intended output is similar to: + +PATH/test-labelled-ranges.c:9: error: mismatching types: 'int' and 'const char *' + 19 | 42 + "foo" + | ~~ ^ ~~~~~ + | | | + | int const char * + + along with the equivalent in SARIF. */ + +#include "libdiagnostics.h" +#include "test-helpers.h" + +/* +_________11111111112 +12345678901234567890 + 42 + "foo" +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + diagnostic_manager_add_text_sink (diag_mgr, stderr, + DIAGNOSTIC_COLORIZE_IF_TTY); + diagnostic_manager_add_sarif_sink (diag_mgr, stderr, + DIAGNOSTIC_SARIF_VERSION_2_1_0); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c"); + const diagnostic_physical_location *loc_operator + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 6); + + + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_ERROR); + diagnostic_set_location (d, loc_operator); + diagnostic_add_location_with_label (d, + make_range (diag_mgr, file, line_num, 3, 4), + "int"); + diagnostic_add_location_with_label (d, + make_range (diag_mgr, file, line_num, 8, 12), + "const char *"); + + diagnostic_finish (d, "mismatching types: %qs and %qs", "int", "const char *"); + + diagnostic_manager_release (diag_mgr); + return 0; +} diff --git a/gcc/testsuite/libdiagnostics.dg/test-logical-location.c b/gcc/testsuite/libdiagnostics.dg/test-logical-location.c new file mode 100644 index 000000000000..c94f54eae686 --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-logical-location.c @@ -0,0 +1,60 @@ +/* Example of using a logical location. + + Intended output is similar to: + +In function 'test_qualified_name': +PATH/test-error-with-note.c:6: error: can't find 'foo' + 6 | PRINT "hello world!"; + | ^~~~~~~~~~~~ + + along with the equivalent in SARIF. */ + +#include "libdiagnostics.h" + +/* Placeholder source: +_________111111111122 +123456789012345678901 +PRINT "hello world!"; +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + diagnostic_manager_add_text_sink (diag_mgr, stderr, + DIAGNOSTIC_COLORIZE_IF_TTY); + diagnostic_manager_add_sarif_sink (diag_mgr, stderr, + DIAGNOSTIC_SARIF_VERSION_2_1_0); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c"); + const diagnostic_physical_location *loc_start + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 8); + const diagnostic_physical_location *loc_end + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 19); + const diagnostic_physical_location *loc_range + = diagnostic_manager_new_location_from_range (diag_mgr, + loc_start, + loc_start, + loc_end); + + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_ERROR); + diagnostic_set_location (d, loc_range); + + const diagnostic_logical_location *logical_loc + = diagnostic_manager_new_logical_location (diag_mgr, + DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION, + NULL, /* parent */ + "test_short_name", + "test_qualified_name", + "test_decorated_name"); + + diagnostic_set_logical_location (d, logical_loc); + + diagnostic_finish (d, "can't find %qs", "foo"); + + diagnostic_manager_release (diag_mgr); + return 0; +}; diff --git a/gcc/testsuite/libdiagnostics.dg/test-metadata.c b/gcc/testsuite/libdiagnostics.dg/test-metadata.c new file mode 100644 index 000000000000..119e2302937c --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-metadata.c @@ -0,0 +1,54 @@ +/* Example of setting a CWE and adding extra metadata. + + Intended output is similar to: + +PATH/test-metadata.c:21: warning: never use 'gets' [CWE-242] [STR34-C] + 21 | gets (buf); + | ^~~~~~~~~~ + + where the metadata tags are linkified in a sufficiently capable terminal, + along with the equivalent in SARIF. */ + +#include "libdiagnostics.h" +#include "test-helpers.h" + +/* Placeholder source: +_________11111111112 +12345678901234567890 +void test_cwe (void) +{ + char buf[1024]; + gets (buf); +} +*/ +const int line_num = __LINE__ - 3; + +int +main () +{ + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + diagnostic_manager_set_tool_name (diag_mgr, "FooChecker"); + diagnostic_manager_set_full_name (diag_mgr, "FooChecker 0.1 (en_US)"); + diagnostic_manager_set_version_string (diag_mgr, "0.1"); + diagnostic_manager_set_version_url (diag_mgr, "https://www.example.com/0.1/"); + + diagnostic_manager_add_text_sink (diag_mgr, stderr, + DIAGNOSTIC_COLORIZE_IF_TTY); + diagnostic_manager_add_sarif_sink (diag_mgr, stderr, + DIAGNOSTIC_SARIF_VERSION_2_1_0); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c"); + const diagnostic_physical_location *loc_token + = make_range (diag_mgr, file, line_num, 3, 12); + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_WARNING); + diagnostic_set_location (d, loc_token); + diagnostic_set_cwe (d, 242); /* CWE-242: Use of Inherently Dangerous Function. */ + diagnostic_add_rule (d, "STR34-C", "https://example.com/"); + + diagnostic_finish (d, "never use %qs", "gets"); + + diagnostic_manager_release (diag_mgr); + return 0; +} diff --git a/gcc/testsuite/libdiagnostics.dg/test-multiple-lines.c b/gcc/testsuite/libdiagnostics.dg/test-multiple-lines.c new file mode 100644 index 000000000000..37399c0e9466 --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-multiple-lines.c @@ -0,0 +1,61 @@ +/* Example of a warning with multiple locations in various source lines, + with an insertion fix-it hint. + + Intended output is similar to: + +/PATH/test-multiple-lines.c:17: warning: missing comma + 16 | const char *strs[3] = {"foo", + | ~~~~~ + 17 | "bar" + | ~~~~~^ + 18 | "baz"}; + | ~~~~~ + + along with the equivalent in SARIF. */ + +#include "libdiagnostics.h" +#include "test-helpers.h" + +/* Placeholder source (missing comma after "bar"): +_________11111111112222222222 +12345678901234567890123456789 +const char *strs[3] = {"foo", + "bar" + "baz"}; +*/ +const int foo_line_num = __LINE__ - 4; + +int +main () +{ + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + diagnostic_manager_add_text_sink (diag_mgr, stderr, + DIAGNOSTIC_COLORIZE_IF_TTY); + diagnostic_manager_add_sarif_sink (diag_mgr, stderr, + DIAGNOSTIC_SARIF_VERSION_2_1_0); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c"); + const diagnostic_physical_location *loc_comma + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, foo_line_num + 1, 29); + const diagnostic_physical_location *loc_foo + = make_range (diag_mgr, file, foo_line_num, 24, 28); + const diagnostic_physical_location *loc_bar + = make_range (diag_mgr, file, foo_line_num + 1, 24, 28); + const diagnostic_physical_location *loc_baz + = make_range (diag_mgr, file, foo_line_num + 2, 24, 28); + + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_WARNING); + diagnostic_set_location (d, loc_comma); + diagnostic_add_location (d, loc_foo); + diagnostic_add_location (d, loc_bar); + diagnostic_add_location (d, loc_baz); + + diagnostic_add_fix_it_hint_insert_after (d, loc_bar, ","); + + diagnostic_finish (d, "missing comma"); + + diagnostic_manager_release (diag_mgr); + return 0; +}; diff --git a/gcc/testsuite/libdiagnostics.dg/test-no-column.c b/gcc/testsuite/libdiagnostics.dg/test-no-column.c new file mode 100644 index 000000000000..e8fd63d90518 --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-no-column.c @@ -0,0 +1,41 @@ +/* Example of emitting an error without a column number. + + Intended output is similar to: + +PATH/test-error-with-note.c:6: error: can't find 'foo' + 6 | PRINT "hello world!"; + + along with the equivalent in SARIF. */ + +#include "libdiagnostics.h" + +/* +_________111111111122 +123456789012345678901 +PRINT "hello world!"; +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + diagnostic_manager_add_text_sink (diag_mgr, stderr, + DIAGNOSTIC_COLORIZE_IF_TTY); + diagnostic_manager_add_sarif_sink (diag_mgr, stderr, + DIAGNOSTIC_SARIF_VERSION_2_1_0); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c"); + const diagnostic_physical_location *loc + = diagnostic_manager_new_location_from_file_and_line (diag_mgr, file, line_num); + + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_ERROR); + diagnostic_set_location (d, loc); + + diagnostic_finish (d, "can't find %qs", "foo"); + + diagnostic_manager_release (diag_mgr); + return 0; +}; diff --git a/gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint.c b/gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint.c new file mode 100644 index 000000000000..7184552ad099 --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-note-with-fix-it-hint.c @@ -0,0 +1,52 @@ +/* Example of a grouped error and note, with a fix-it hint on the note. + + Intended output is similar to: + +/PATH/test-note-with-fix-it-hint.c:19: error: unknown field 'colour' + 19 | return p->colour; + | ^~~~~~ +/PATH/test-note-with-fix-it-hint.c:19: note: did you mean 'color' + 19 | return p->colour; + | ^~~~~~ + | color + + along with the equivalent in SARIF. */ + +#include "libdiagnostics.h" +#include "test-helpers.h" + +/* Placeholder source: +_________11111111112 +12345678901234567890 + return p->colour; +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + diagnostic_manager_add_text_sink (diag_mgr, stderr, DIAGNOSTIC_COLORIZE_IF_TTY); + diagnostic_manager_add_sarif_sink (diag_mgr, stderr, DIAGNOSTIC_SARIF_VERSION_2_1_0); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c"); + const diagnostic_physical_location *loc_token + = make_range (diag_mgr, file, line_num, 13, 18); + + diagnostic_manager_begin_group (diag_mgr); + + diagnostic *err = diagnostic_begin (diag_mgr, DIAGNOSTIC_LEVEL_ERROR); + diagnostic_set_location (err, loc_token); + diagnostic_finish (err, "unknown field %qs", "colour"); + + diagnostic *n = diagnostic_begin (diag_mgr, DIAGNOSTIC_LEVEL_NOTE); + diagnostic_set_location (n, loc_token); + diagnostic_add_fix_it_hint_replace (n, loc_token, "color"); + diagnostic_finish (n, "did you mean %qs", "color"); + + diagnostic_manager_end_group (diag_mgr); + + diagnostic_manager_release (diag_mgr); + return 0; +} diff --git a/gcc/testsuite/libdiagnostics.dg/test-text-sink-options.c b/gcc/testsuite/libdiagnostics.dg/test-text-sink-options.c new file mode 100644 index 000000000000..2c4dc8aeac01 --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-text-sink-options.c @@ -0,0 +1,46 @@ +/* Example of controlling options for text sinks. */ + +#include "libdiagnostics.h" + +/* +_________111111111122 +123456789012345678901 +PRINT "hello world!"; +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + diagnostic_text_sink *sink_1 + = diagnostic_manager_add_text_sink (diag_mgr, stderr, + DIAGNOSTIC_COLORIZE_IF_TTY); + diagnostic_text_sink_set_source_printing_enabled (sink_1, 0); + + diagnostic_text_sink *sink_2 + = diagnostic_manager_add_text_sink (diag_mgr, stderr, + DIAGNOSTIC_COLORIZE_IF_TTY); + diagnostic_text_sink_set_labelled_source_colorization_enabled (sink_2, 0); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c"); + const diagnostic_physical_location *loc_start + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 8); + const diagnostic_physical_location *loc_end + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 19); + const diagnostic_physical_location *loc_range + = diagnostic_manager_new_location_from_range (diag_mgr, + loc_start, + loc_start, + loc_end); + + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_ERROR); + diagnostic_set_location (d, loc_range); + + diagnostic_finish (d, "can't find %qs", "foo"); + + diagnostic_manager_release (diag_mgr); + return 0; +}; diff --git a/gcc/testsuite/libdiagnostics.dg/test-warning.c b/gcc/testsuite/libdiagnostics.dg/test-warning.c new file mode 100644 index 000000000000..cef1db3cfced --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-warning.c @@ -0,0 +1,52 @@ +/* Example of emitting a warning. + + Intended output is similar to: + +/PATH/test-warning.c:15: warning: this is a warning + 15 | PRINT "hello world!"; + | ^~~~~~~~~~~~ + + along with the equivalent in SARIF. */ + +#include "libdiagnostics.h" + +/* +_________111111111122 +123456789012345678901 +PRINT "hello world!"; +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + diagnostic_manager_add_text_sink (diag_mgr, stderr, + DIAGNOSTIC_COLORIZE_IF_TTY); + diagnostic_manager_add_sarif_sink (diag_mgr, stderr, + DIAGNOSTIC_SARIF_VERSION_2_1_0); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, + __FILE__, + "c"); + + const diagnostic_physical_location *loc_start + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 8); + const diagnostic_physical_location *loc_end + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 19); + const diagnostic_physical_location *loc_range + = diagnostic_manager_new_location_from_range (diag_mgr, + loc_start, + loc_start, + loc_end); + + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_WARNING); + diagnostic_set_location (d, loc_range); + + diagnostic_finish (d, "this is a warning"); + + diagnostic_manager_release (diag_mgr); + return 0; +}; diff --git a/gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file.c b/gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file.c new file mode 100644 index 000000000000..2cde6947a6df --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-write-sarif-to-file.c @@ -0,0 +1,46 @@ +/* Example of writing diagnostics as SARIF to a file. */ + +#include "libdiagnostics.h" + +/* +_________111111111122 +123456789012345678901 +PRINT "hello world!"; +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + FILE *outfile = fopen ("test.txt", "w"); + if (!outfile) + return -1; + + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + diagnostic_manager_add_sarif_sink (diag_mgr, outfile, + DIAGNOSTIC_SARIF_VERSION_2_1_0); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c"); + const diagnostic_physical_location *loc_start + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 8); + const diagnostic_physical_location *loc_end + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 19); + const diagnostic_physical_location *loc_range + = diagnostic_manager_new_location_from_range (diag_mgr, + loc_start, + loc_start, + loc_end); + + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_ERROR); + diagnostic_set_location (d, loc_range); + + diagnostic_finish (d, "can't find %qs", "foo"); + + diagnostic_manager_release (diag_mgr); + + fclose (outfile); + + return 0; +}; diff --git a/gcc/testsuite/libdiagnostics.dg/test-write-text-to-file.c b/gcc/testsuite/libdiagnostics.dg/test-write-text-to-file.c new file mode 100644 index 000000000000..8ad448c8c9ce --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/test-write-text-to-file.c @@ -0,0 +1,47 @@ +/* Example of writing diagnostics in text form, but to a file, + rather than stderr. */ + +#include "libdiagnostics.h" + +/* +_________111111111122 +123456789012345678901 +PRINT "hello world!"; +*/ +const int line_num = __LINE__ - 2; + +int +main () +{ + FILE *outfile = fopen ("test.txt", "w"); + if (!outfile) + return -1; + + diagnostic_manager *diag_mgr = diagnostic_manager_new (); + + diagnostic_manager_add_text_sink (diag_mgr, outfile, + DIAGNOSTIC_COLORIZE_NO); + + const diagnostic_file *file = diagnostic_manager_new_file (diag_mgr, __FILE__, "c"); + const diagnostic_physical_location *loc_start + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 8); + const diagnostic_physical_location *loc_end + = diagnostic_manager_new_location_from_file_line_column (diag_mgr, file, line_num, 19); + const diagnostic_physical_location *loc_range + = diagnostic_manager_new_location_from_range (diag_mgr, + loc_start, + loc_start, + loc_end); + + diagnostic *d = diagnostic_begin (diag_mgr, + DIAGNOSTIC_LEVEL_ERROR); + diagnostic_set_location (d, loc_range); + + diagnostic_finish (d, "can't find %qs", "foo"); + + diagnostic_manager_release (diag_mgr); + + fclose (outfile); + + return 0; +}; From patchwork Mon Nov 6 22:29:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 162206 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:8f47:0:b0:403:3b70:6f57 with SMTP id j7csp2966324vqu; Mon, 6 Nov 2023 14:32:24 -0800 (PST) X-Google-Smtp-Source: AGHT+IE9z/F+HG+pw5F13S9/UbbJkPrIb8VuIJuMl5iNqM2bdjupZStTwofwh6d9mXI8Q9p4kpOB X-Received: by 2002:a05:6830:2b22:b0:6d3:28c1:bd46 with SMTP id l34-20020a0568302b2200b006d328c1bd46mr15273754otv.10.1699309944680; Mon, 06 Nov 2023 14:32:24 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1699309944; cv=pass; d=google.com; s=arc-20160816; b=Hie1h5LDdf/wwrmq6cNVnMlQO/p/DERT12DPUtmebWed+RFv1L6a67z9PDWdisovnm YqGkwRpKyMtvQUj3yjEkGy6iIil5uidbUM+1jT8NfBHag/FL3VlcMLFK4cMe4sHbEYWe dfQQDck5bQ+lzyXaVIDiZyED7KgzqwA/kmcHOG32OCfS6G1YvHXsQHZZwZG/4laCH166 xW1Y/AeTmhKWXgGnQeImAYLiH31RPR2gGn8ZOLuwOLum6pRn71SFGxvl8busPl1iGCJy xoZCokCQDZ3RCiafOmambT3r+GHXo79Wgu+9l4Tyh9QeGWHBFOB58D+m661UuQI8tg/s PM6A== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature:arc-filter:dmarc-filter:delivered-to; bh=tRnK28vswCgwVvn/mhaLh/k46QQdCeMEnw63fCt80u0=; fh=rfR2SIXf27jLXVYT4RHMSVZgLIsrp2E1IHKtUM1Ws70=; b=sILjdQ2PRPAF5r48xM5jF4TXFLOgoG19G5xJ6l0FdXZz7M9jOpwNgRaixKqS9pyfJ7 80uImjJ2tgxl0nDZFI6OE6kQ+baem8IfRECgmjGM/0YOOcXGbThy94J2zNoOvkwdY1ZN dCeqQdQp3gdRYDNlVKiKJVjt0EhHirFtsUiMFkBuFZyHY6MrJlO4O41cl0MjxqmqkSjp RJ8eoNj5CRJthRmct875YLfcw4QjfT/pJsml3r0e/JTa/B7H/yzNffxkvN29zsH66OEz 92gVHb6PYS5rTSDNziTkmI3Povi6ksYocs10Pclb8JZdtDzr0HLh7avVzG/jvvKRuqWK gx9Q== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=ES6rTEW8; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (server2.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id z7-20020a05622a028700b00403abf5b44csi6428149qtw.264.2023.11.06.14.32.24 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 06 Nov 2023 14:32:24 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=ES6rTEW8; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id C14F8388266F for ; Mon, 6 Nov 2023 22:31:30 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org 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 70E27385841D for ; Mon, 6 Nov 2023 22:30:14 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 70E27385841D Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 70E27385841D Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1699309821; cv=none; b=ODqnEwi8iL20KB1puJ8W3r2/v7oRXZrRlAb0AOZ+D0FGK/LW0/jfeesA0GMQ4Li6gvghzq9wotOVaOOj5nwLZqcP1Wl1aDMsd9WXHztUpiiiQkn1jATWeWNn4RG0fW4X9xE5zU3cpPQkb6ePgBXu5w2SeSYWhCLvHrs6C1nAc/A= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1699309821; c=relaxed/simple; bh=nb0mGhL4vqEj3sRDgPkMHcxYS/fwBJvWzE/ry172FoA=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=Dycb+zgvyaC0vqmOlwqUG41AYPjkRO+3UNOXI2pKPtHtXu8BJdYNtIL2yecTgF2V9eOLtC4jyywfNQpBeVp4VidjH7/dyyuuGQIGLBpNUEZthcyq2dwppNBlv1nr9s6sF+d3oDgoD7ln6zhY/PXKJEBOkbr+5Hb051ZSUaj3U34= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1699309814; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=tRnK28vswCgwVvn/mhaLh/k46QQdCeMEnw63fCt80u0=; b=ES6rTEW83sWl27M/Wp/y9DPCSscQEgU4dBhbpB1HZ4AET6pd430FlW2hukDJRso0RPaKu7 zHsskCgoz1Atcwl4Oe+wHNnk7XZu6lwg4KyQPjdBw5tC3rMN67KAl8tQYyyL3iuMv5Teob a0Y94cvUmnp5KjdLDlltiscKKoGU2Qk= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-558-hXqfhPJ4PQKt5wLsjC3ffQ-1; Mon, 06 Nov 2023 17:30:12 -0500 X-MC-Unique: hXqfhPJ4PQKt5wLsjC3ffQ-1 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.rdu2.redhat.com [10.11.54.2]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 77C50811E7D; Mon, 6 Nov 2023 22:30:12 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.22.8.62]) by smtp.corp.redhat.com (Postfix) with ESMTP id 2834040C6EB9; Mon, 6 Nov 2023 22:30:12 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org, binutils@sourceware.org Cc: Nick Clifton , Simon Sobisch , David Malcolm Subject: [PATCH 2/2] libdiagnostics: work-in-progress implementation Date: Mon, 6 Nov 2023 17:29:58 -0500 Message-Id: <20231106222959.2707741-3-dmalcolm@redhat.com> In-Reply-To: <20231106222959.2707741-1-dmalcolm@redhat.com> References: <20231106222959.2707741-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.2 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.4 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_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1781855624394151016 X-GMAIL-MSGID: 1781855624394151016 Here's a work-in-progress patch for GCC that adds the implementation of libdiagnostics. Various aspects of this need work; posting now for early feedback on overall direction. For example, the testsuite doesn't yet check the output from the test client programs (and I'm not quite sure of the best way to express that in DejaGnu). gcc/ChangeLog: * Makefile.in (lang_checks): Add check-libdiagnostics. (start.encap): Add libdiagnostics. (libdiagnostics_OBJS): New. ...plus a bunch of stuff hacked up from jit/Make-lang.in. * configure: Regenerate. * configure.ac (check_languages): Add check-libdiagnostics. * input.h: Add FIXME. * libdiagnostics.cc: New file. * libdiagnostics.map: New file. gcc/testsuite/ChangeLog: * libdiagnostics.dg/libdiagnostics.exp: New, based on jit.exp. --- gcc/Makefile.in | 134 +- gcc/configure | 2 +- gcc/configure.ac | 2 +- gcc/input.h | 2 +- gcc/libdiagnostics.cc | 1124 +++++++++++++++++ gcc/libdiagnostics.map | 57 + .../libdiagnostics.dg/libdiagnostics.exp | 544 ++++++++ 7 files changed, 1860 insertions(+), 5 deletions(-) create mode 100644 gcc/libdiagnostics.cc create mode 100644 gcc/libdiagnostics.map create mode 100644 gcc/testsuite/libdiagnostics.dg/libdiagnostics.exp diff --git a/gcc/Makefile.in b/gcc/Makefile.in index ff77d3cdc64..8f93ae48024 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -611,7 +611,7 @@ host_xm_defines=@host_xm_defines@ xm_file_list=@xm_file_list@ xm_include_list=@xm_include_list@ xm_defines=@xm_defines@ -lang_checks= +lang_checks=check-libdiagnostics lang_checks_parallelized= lang_opt_files=@lang_opt_files@ $(srcdir)/c-family/c.opt $(srcdir)/common.opt $(srcdir)/params.opt $(srcdir)/analyzer/analyzer.opt lang_specs_files=@lang_specs_files@ @@ -2153,7 +2153,7 @@ all.cross: native gcc-cross$(exeext) cpp$(exeext) specs \ libgcc-support lang.all.cross doc selftest @GENINSRC@ srcextra # This is what must be made before installing GCC and converting libraries. start.encap: native xgcc$(exeext) cpp$(exeext) specs \ - libgcc-support lang.start.encap @GENINSRC@ srcextra + libgcc-support lang.start.encap libdiagnostics @GENINSRC@ srcextra # These can't be made until after GCC can run. rest.encap: lang.rest.encap # This is what is made with the host's compiler @@ -2242,6 +2242,136 @@ cpp$(exeext): $(GCC_OBJS) c-family/cppspec.o libcommon-target.a $(LIBDEPS) \ c-family/cppspec.o $(EXTRA_GCC_OBJS) libcommon-target.a \ $(EXTRA_GCC_LIBS) $(LIBS) + +libdiagnostics_OBJS = libdiagnostics.o \ + libcommon.a + +# FIXME: +# Define the names for selecting jit in LANGUAGES. +# Note that it would be nice to move the dependency on g++ +# into the jit rule, but that needs a little bit of work +# to do the right thing within all.cross. + +LIBDIAGNOSTICS_VERSION_NUM = 0 +LIBDIAGNOSTICS_MINOR_NUM = 0 +LIBDIAGNOSTICS_RELEASE_NUM = 1 + +ifneq (,$(findstring mingw,$(target))) +LIBDIAGNOSTICS_FILENAME = libdiagnostics-$(LIBDIAGNOSTICS_VERSION_NUM).dll +LIBDIAGNOSTICS_IMPORT_LIB = libdiagnostics.dll.a + +libdiagnostics: $(LIBDIAGNOSTICS_FILENAME) \ + $(FULL_DRIVER_NAME) + +else + +ifneq (,$(findstring darwin,$(host))) + +LIBDIAGNOSTICS_AGE = 1 +LIBDIAGNOSTICS_BASENAME = libdiagnostics + +LIBDIAGNOSTICS_SONAME = \ + ${libdir}/$(LIBDIAGNOSTICS_BASENAME).$(LIBDIAGNOSTICS_VERSION_NUM).dylib +LIBDIAGNOSTICS_FILENAME = $(LIBDIAGNOSTICS_BASENAME).$(LIBDIAGNOSTICS_VERSION_NUM).dylib +LIBDIAGNOSTICS_LINKER_NAME = $(LIBDIAGNOSTICS_BASENAME).dylib + +# Conditionalize the use of the LD_VERSION_SCRIPT_OPTION and +# LD_SONAME_OPTION depending if configure found them, using $(if) +# We have to define a LIBDIAGNOSTICS_COMMA here, otherwise the commas in the "true" +# result are treated as separators by the $(if). +LIBDIAGNOSTICS_COMMA := , +LIBDIAGNOSTICS_VERSION_SCRIPT_OPTION = \ + $(if $(LD_VERSION_SCRIPT_OPTION),\ + -Wl$(LIBDIAGNOSTICS_COMMA)$(LD_VERSION_SCRIPT_OPTION)$(LIBDIAGNOSTICS_COMMA)$(srcdir)/libdiagnostics.map) + +LIBDIAGNOSTICS_SONAME_OPTION = \ + $(if $(LD_SONAME_OPTION), \ + -Wl$(LIBDIAGNOSTICS_COMMA)$(LD_SONAME_OPTION)$(LIBDIAGNOSTICS_COMMA)$(LIBDIAGNOSTICS_SONAME)) + +LIBDIAGNOSTICS_SONAME_SYMLINK = $(LIBDIAGNOSTICS_FILENAME) +LIBDIAGNOSTICS_LINKER_NAME_SYMLINK = $(LIBDIAGNOSTICS_LINKER_NAME) + +libdiagnostics: $(LIBDIAGNOSTICS_FILENAME) \ + $(LIBDIAGNOSTICS_SYMLINK) \ + $(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK) \ + $(FULL_DRIVER_NAME) + +else + +LIBDIAGNOSTICS_LINKER_NAME = libdiagnostics.so +LIBDIAGNOSTICS_SONAME = $(LIBDIAGNOSTICS_LINKER_NAME).$(LIBDIAGNOSTICS_VERSION_NUM) +LIBDIAGNOSTICS_FILENAME = \ + $(LIBDIAGNOSTICS_SONAME).$(LIBDIAGNOSTICS_MINOR_NUM).$(LIBDIAGNOSTICS_RELEASE_NUM) + +LIBDIAGNOSTICS_LINKER_NAME_SYMLINK = $(LIBDIAGNOSTICS_LINKER_NAME) +LIBDIAGNOSTICS_SONAME_SYMLINK = $(LIBDIAGNOSTICS_SONAME) + +# Conditionalize the use of the LD_VERSION_SCRIPT_OPTION and +# LD_SONAME_OPTION depending if configure found them, using $(if) +# We have to define a LIBDIAGNOSTICS_COMMA here, otherwise the commas in the "true" +# result are treated as separators by the $(if). +LIBDIAGNOSTICS_COMMA := , +LIBDIAGNOSTICS_VERSION_SCRIPT_OPTION = \ + $(if $(LD_VERSION_SCRIPT_OPTION),\ + -Wl$(LIBDIAGNOSTICS_COMMA)$(LD_VERSION_SCRIPT_OPTION)$(LIBDIAGNOSTICS_COMMA)$(srcdir)/libdiagnostics.map) + +LIBDIAGNOSTICS_SONAME_OPTION = \ + $(if $(LD_SONAME_OPTION), \ + -Wl$(LIBDIAGNOSTICS_COMMA)$(LD_SONAME_OPTION)$(LIBDIAGNOSTICS_COMMA)$(LIBDIAGNOSTICS_SONAME)) + +libdiagnostics: $(LIBDIAGNOSTICS_FILENAME) \ + $(LIBDIAGNOSTICS_SYMLINK) \ + $(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK) \ + $(FULL_DRIVER_NAME) + +endif +endif + +libdiagnostics.serial = $(LIBDIAGNOSTICS_FILENAME) + +# Tell GNU make to ignore these if they exist. +.PHONY: libdiagnostics + +ifneq (,$(findstring mingw,$(target))) +# Create import library +LIBDIAGNOSTICS_EXTRA_OPTS = -Wl,--out-implib,$(LIBDIAGNOSTICS_IMPORT_LIB) +else + +ifneq (,$(findstring darwin,$(host))) +# TODO : Construct a Darwin-style symbol export file. +LIBDIAGNOSTICS_EXTRA_OPTS = -Wl,-compatibility_version,$(LIBDIAGNOSTICS_VERSION_NUM) \ + -Wl,-current_version,$(LIBDIAGNOSTICS_VERSION_NUM).$(LIBDIAGNOSTICS_MINOR_NUM).$(LIBDIAGNOSTICS_AGE) \ + $(LIBDIAGNOSTICS_VERSION_SCRIPT_OPTION) \ + $(LIBDIAGNOSTICS_SONAME_OPTION) +else + +LIBDIAGNOSTICS_EXTRA_OPTS = $(LIBDIAGNOSTICS_VERSION_SCRIPT_OPTION) \ + $(LIBDIAGNOSTICS_SONAME_OPTION) +endif +endif + +$(LIBDIAGNOSTICS_FILENAME): $(libdiagnostics_OBJS) $(CPPLIB) $(EXTRA_GCC_LIBS) $(LIBS) \ + $(LIBDEPS) $(srcdir)/libdiagnostics.map + @$(call LINK_PROGRESS,$(INDEX.libdiagnostics),start) + +$(LLINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ -shared \ + $(libdiagnostics_OBJS) \ + $(CPPLIB) $(EXTRA_GCC_LIBS) $(LIBS) \ + $(LIBDIAGNOSTICS_EXTRA_OPTS) + @$(call LINK_PROGRESS,$(INDEX.libdiagnostics),end) + +# Create symlinks when not building for Windows +ifeq (,$(findstring mingw,$(target))) + +ifeq (,$(findstring darwin,$(host))) +# but only one level for Darwin, version info is embedded. +$(LIBDIAGNOSTICS_SONAME_SYMLINK): $(LIBDIAGNOSTICS_FILENAME) + ln -sf $(LIBDIAGNOSTICS_FILENAME) $(LIBDIAGNOSTICS_SONAME_SYMLINK) +endif + +$(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK): $(LIBDIAGNOSTICS_SONAME_SYMLINK) + ln -sf $(LIBDIAGNOSTICS_SONAME_SYMLINK) $(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK) +endif + # Dump a specs file to make -B./ read these specs over installed ones. $(SPECS): xgcc$(exeext) $(GCC_FOR_TARGET) -dumpspecs > tmp-specs diff --git a/gcc/configure b/gcc/configure index 77f33ee4df6..abcb70b1b5f 100755 --- a/gcc/configure +++ b/gcc/configure @@ -31997,7 +31997,7 @@ $as_echo "#define ENABLE_LTO 1" >>confdefs.h esac done -check_languages= +check_languages=check-libdiagnostics for language in $all_selected_languages do check_languages="$check_languages check-$language" diff --git a/gcc/configure.ac b/gcc/configure.ac index 10982cdfc09..5968670b2d6 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -7281,7 +7281,7 @@ changequote([,])dnl esac done -check_languages= +check_languages=check-libdiagnostics for language in $all_selected_languages do check_languages="$check_languages check-$language" diff --git a/gcc/input.h b/gcc/input.h index 5eac1dc40a6..e4d230cc112 100644 --- a/gcc/input.h +++ b/gcc/input.h @@ -113,10 +113,10 @@ class char_span size_t m_n_elts; }; +// FIXME: eliminate these; use global_dc->m_file_cache extern char_span location_get_source_line (const char *file_path, int line); extern char *get_source_text_between (location_t, location_t); extern char_span get_source_file_content (const char *file_path); - extern bool location_missing_trailing_newline (const char *file_path); /* Forward decl of slot within file_cache, so that the definition doesn't diff --git a/gcc/libdiagnostics.cc b/gcc/libdiagnostics.cc new file mode 100644 index 00000000000..788b28edc53 --- /dev/null +++ b/gcc/libdiagnostics.cc @@ -0,0 +1,1124 @@ +/* C++ implementation of a pure C API for emitting diagnostics. + Copyright (C) 2023 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 +. */ + +#include "config.h" +#define INCLUDE_MEMORY +#define INCLUDE_VECTOR +#include "system.h" +#include "coretypes.h" +#include "intl.h" +#include "diagnostic.h" +#include "diagnostic-color.h" +#include "diagnostic-url.h" +#include "diagnostic-metadata.h" +#include "diagnostic-client-data-hooks.h" +#include "logical-location.h" +#include "edit-context.h" +#include "make-unique.h" +#include "libdiagnostics.h" + +class owned_nullable_string +{ +public: + owned_nullable_string () : m_str (nullptr) {} + owned_nullable_string (const char *str) + : m_str (str ? ::xstrdup (str) : nullptr) + { + } + + ~owned_nullable_string () + { + free (m_str); + } + + void set (const char *str) + { + free (m_str); + m_str = str ? ::xstrdup (str) : nullptr; + } + + const char *get_str () const { return m_str; } + + char *xstrdup () const + { + return m_str ? ::xstrdup (m_str) : nullptr; + } + +private: + char *m_str; +}; + +/* This has to be a "struct" as it is exposed in the C API. */ + +struct diagnostic_file +{ + diagnostic_file (const char *name, const char *sarif_source_language) + : m_name (name), m_sarif_source_language (sarif_source_language) + { + } + + const char *get_name () const { return m_name.get_str (); } + const char *get_sarif_source_language () const + { + return m_sarif_source_language.get_str (); + } + +private: + owned_nullable_string m_name; + owned_nullable_string m_sarif_source_language; +}; + +/* This has to be a "struct" as it is exposed in the C API. */ + +struct diagnostic_logical_location : public logical_location +{ + diagnostic_logical_location (enum diagnostic_logical_location_kind_t kind, + const diagnostic_logical_location *parent, + const char *short_name, + const char *fully_qualified_name, + const char *decorated_name) + : m_kind (kind), + m_parent (parent), + m_short_name (short_name), + m_fully_qualified_name (fully_qualified_name), + m_decorated_name (decorated_name) + { + } + + const char *get_short_name () const final override + { + return m_short_name.get_str (); + } + const char *get_name_with_scope () const final override + { + return m_fully_qualified_name.get_str (); + } + const char *get_internal_name () const final override + { + return m_decorated_name.get_str (); + } + enum logical_location_kind get_kind () const final override + { + switch (m_kind) + { + default: + gcc_unreachable (); + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION: + return LOGICAL_LOCATION_KIND_FUNCTION; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER: + return LOGICAL_LOCATION_KIND_MEMBER; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE: + return LOGICAL_LOCATION_KIND_MODULE; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE: + return LOGICAL_LOCATION_KIND_NAMESPACE; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE: + return LOGICAL_LOCATION_KIND_TYPE; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE: + return LOGICAL_LOCATION_KIND_RETURN_TYPE; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER: + return LOGICAL_LOCATION_KIND_PARAMETER; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE: + return LOGICAL_LOCATION_KIND_VARIABLE; + } + } + +private: + enum diagnostic_logical_location_kind_t m_kind; + const diagnostic_logical_location *m_parent; + owned_nullable_string m_short_name; + owned_nullable_string m_fully_qualified_name; + owned_nullable_string m_decorated_name; +}; + +class sink +{ +public: + virtual ~sink (); + + void begin_group () + { + m_dc.begin_group (); + } + void end_group () + { + m_dc.end_group (); + } + + void emit (diagnostic &diag, const char *msgid, va_list *args) + LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING(3, 0); + +protected: + sink (diagnostic_manager &mgr); + + static char * + get_option_name_cb (diagnostic_context *, int, diagnostic_t, diagnostic_t) + { + return nullptr; // FIXME + } + + diagnostic_manager &m_mgr; + + /* One context per sink. */ + diagnostic_context m_dc; +}; + +class text_sink : public sink +{ +public: + text_sink (diagnostic_manager &mgr, + FILE *dst_stream, + enum diagnostic_colorize colorize); + + void + on_begin_text_diagnostic (diagnostic_info *info); + +private: + const diagnostic_logical_location *m_current_logical_loc; +}; + +class sarif_sink : public sink +{ +public: + sarif_sink (diagnostic_manager &mgr, + FILE *dst_stream, + enum diagnostic_sarif_version version); +}; + +/* Helper for the linemap code. */ + +static size_t +round_alloc_size (size_t s) +{ + return s; +} + +class impl_diagnostic_client_data_hooks : public diagnostic_client_data_hooks +{ +public: + impl_diagnostic_client_data_hooks (diagnostic_manager &mgr) + : m_mgr (mgr) + {} + + 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 add_sarif_invocation_properties (sarif_object &invocation_obj) + const final override; + +private: + diagnostic_manager &m_mgr; +}; + +class impl_client_version_info : public client_version_info +{ +public: + const char *get_tool_name () const final override + { + return m_name.get_str (); + } + + char *maybe_make_full_name () const final override + { + return m_full_name.xstrdup (); + } + + const char *get_version_string () const final override + { + return m_version.get_str (); + } + + char *maybe_make_version_url () const final override + { + return m_version_url.xstrdup (); + } + + void for_each_plugin (plugin_visitor &) const final override + { + // No-op. + } + + owned_nullable_string m_name; + owned_nullable_string m_full_name; + owned_nullable_string m_version; + owned_nullable_string m_version_url; +}; + +/* This has to be a "struct" as it is exposed in the C API. */ + +struct diagnostic_manager +{ +public: + diagnostic_manager () + : m_current_diag (nullptr) + { + linemap_init (&m_line_table, BUILTINS_LOCATION); + m_line_table.m_reallocator = xrealloc; + m_line_table.m_round_alloc_size = round_alloc_size; + m_line_table.default_range_bits = 5; + } + ~diagnostic_manager () + { + /* Clean up sinks first, as they can use other fields. */ + for (size_t i = 0; i < m_sinks.size (); i++) + m_sinks[i] = nullptr; + + for (auto iter : m_str_to_file_map) + delete iter.second; + } + + line_maps *get_line_table () { return &m_line_table; } + file_cache *get_file_cache () { return &m_file_cache; } + + void write_patch (FILE *dst_stream); + + void add_sink (std::unique_ptr sink) + { + m_sinks.push_back (std::move (sink)); + } + + void emit (diagnostic &diag, const char *msgid, va_list *args) + LIBDIAGNOSTICS_PARAM_GCC_FORMAT_STRING(3, 0); + + const diagnostic_file * + new_file (const char *name, + const char *sarif_source_language) + { + if (diagnostic_file **slot = m_str_to_file_map.get (name)) + return *slot; + diagnostic_file *file = new diagnostic_file (name, sarif_source_language); + m_str_to_file_map.put (file->get_name (), file); + return file; + } + + diagnostic_location_t + new_location_from_file_and_line (const diagnostic_file *file, + diagnostic_line_num_t linenum) + { + // FIXME: this is a hack... + /* Build a simple linemap describing some locations. */ + if (LINEMAPS_ORDINARY_USED (&m_line_table) == 0) + linemap_add (&m_line_table, LC_ENTER, false, file->get_name (), 0); + else + { + const line_map *map + = linemap_add (&m_line_table, LC_RENAME_VERBATIM, false, + file->get_name (), 0); + // FIXME: + ((line_map_ordinary *)map)->included_from = UNKNOWN_LOCATION; + } + linemap_line_start (&m_line_table, linenum, 100); + return linemap_position_for_column (&m_line_table, 0); + } + + + diagnostic_location_t + new_location_from_file_line_column (const diagnostic_file *file, + diagnostic_line_num_t line_num, + diagnostic_column_num_t column_num) + { + // FIXME: this is a hack... + /* Build a simple linemap describing some locations. */ + linemap_add (&m_line_table, LC_ENTER, false, file->get_name (), 0); + linemap_line_start (&m_line_table, line_num, 100); + return linemap_position_for_column (&m_line_table, column_num); + } + + diagnostic_location_t + new_location_from_range (diagnostic_location_t loc_caret, + diagnostic_location_t loc_start, + diagnostic_location_t loc_end) + { + return m_line_table.make_location (loc_caret, loc_start, loc_end); + } + + const diagnostic_logical_location * + new_logical_location (enum diagnostic_logical_location_kind_t kind, + const diagnostic_logical_location *parent, + const char *short_name, + const char *fully_qualified_name, + const char *decorated_name) + { + std::unique_ptr logical_loc + = ::make_unique (kind, + parent, + short_name, + fully_qualified_name, + decorated_name); + const diagnostic_logical_location *result = logical_loc.get (); + m_logical_locs.push_back (std::move (logical_loc)); + return result; + } + + void begin_group () + { + for (auto &sink : m_sinks) + sink->begin_group (); + } + + void end_group () + { + for (auto &sink : m_sinks) + sink->end_group (); + } + + const char * + maybe_get_sarif_source_language (const char *filename) + { + if (diagnostic_file **slot = m_str_to_file_map.get (filename)) + { + gcc_assert (*slot); + return (*slot)->get_sarif_source_language (); + } + return nullptr; + } + + const diagnostic *get_current_diag () { return m_current_diag; } + + const client_version_info *get_client_version_info () const + { + return &m_client_version_info; + } + impl_client_version_info *get_client_version_info () + { + return &m_client_version_info; + } + + void + assert_valid_diagnostic_location_t (diagnostic_location_t loc) const + { + // TODO + (void)loc; + } + + /* FIXME: Various things still used the "line_table" global variable. + Set it to be this diagnostic_manager's m_line_table. + Ideally we should eliminate this global (and this function). */ + void set_line_table_global () + { + line_table = &m_line_table; + } + +private: + line_maps m_line_table; + file_cache m_file_cache; + impl_client_version_info m_client_version_info; + std::vector> m_sinks; + hash_map m_str_to_file_map; + std::vector> m_logical_locs; + const diagnostic *m_current_diag; + edit_context m_edit_context; +}; + +class impl_rich_location : public rich_location +{ +public: + impl_rich_location (line_maps *set) + : rich_location (set, UNKNOWN_LOCATION) + {} +}; + +class impl_range_label : public range_label +{ +public: + impl_range_label (const char *text) + : m_text (xstrdup (text)) + {} + + ~impl_range_label () { free (m_text); } + + label_text get_text (unsigned) const final override + { + return label_text::borrow (m_text); + } + +private: + char *m_text; +}; + +class impl_rule : public diagnostic_metadata::rule +{ +public: + impl_rule (const char *title, const char *url) + : m_title (title), + m_url (url) + { + } + + char *make_description () const final override + { + return m_title.xstrdup (); + } + + char *make_url () const final override + { + return m_url.xstrdup (); + } + +private: + owned_nullable_string m_title; + owned_nullable_string m_url; +}; + +/* This has to be a "struct" as it is exposed in the C API. */ + +struct diagnostic +{ +public: + diagnostic (diagnostic_manager &diag_mgr, + enum diagnostic_level level) + : m_diag_mgr (diag_mgr), + m_level (level), + m_rich_loc (diag_mgr.get_line_table ()), + m_logical_loc (nullptr) + {} + + diagnostic_manager &get_manager () const + { + return m_diag_mgr; + } + + enum diagnostic_level get_level () const { return m_level; } + + rich_location *get_rich_location () { return &m_rich_loc; } + const diagnostic_metadata *get_metadata () { return &m_metadata; } + +#if 0 + diagnostic_location_t get_location () const { return m_loc; } +#endif + + void set_cwe (unsigned cwe_id) + { + m_metadata.add_cwe (cwe_id); + } + + void add_rule (const char *title, + const char *url) + { + std::unique_ptr rule = ::make_unique (title, url); + m_metadata.add_rule (*rule.get ()); + m_rules.push_back (std::move (rule)); + } + + void set_location (diagnostic_location_t loc) + { + m_rich_loc.set_range (0, loc, SHOW_RANGE_WITH_CARET); + } + + void + add_location (diagnostic_location_t loc) + { + m_rich_loc.add_range (loc, SHOW_RANGE_WITHOUT_CARET); + } + + void + add_location_with_label (diagnostic_location_t loc, + const char *text) + { + std::unique_ptr label = ::make_unique (text); + m_rich_loc.add_range (loc, + SHOW_RANGE_WITHOUT_CARET, + label.get ()); + m_labels.push_back (std::move (label)); + } + + void + set_logical_location (const diagnostic_logical_location *logical_loc) + { + m_logical_loc = logical_loc; + } + const diagnostic_logical_location *get_logical_location () const + { + return m_logical_loc; + } + +private: + diagnostic_manager &m_diag_mgr; + enum diagnostic_level m_level; + impl_rich_location m_rich_loc; + const diagnostic_logical_location *m_logical_loc; + diagnostic_metadata m_metadata; + std::vector> m_labels; + std::vector> m_rules; +}; + +static diagnostic_t +diagnostic_t_from_diagnostic_level (enum diagnostic_level level) +{ + switch (level) + { + default: + gcc_unreachable (); + case DIAGNOSTIC_LEVEL_ERROR: + return DK_ERROR; + case DIAGNOSTIC_LEVEL_WARNING: + return DK_WARNING; + case DIAGNOSTIC_LEVEL_NOTE: + return DK_NOTE; + } +} + +/* class impl_diagnostic_client_data_hooks. */ + +const client_version_info * +impl_diagnostic_client_data_hooks::get_any_version_info () const +{ + return m_mgr.get_client_version_info (); +} + +const logical_location * +impl_diagnostic_client_data_hooks::get_current_logical_location () const +{ + gcc_assert (m_mgr.get_current_diag ()); + + return m_mgr.get_current_diag ()->get_logical_location (); +} + +const char * +impl_diagnostic_client_data_hooks:: +maybe_get_sarif_source_language (const char *filename) const +{ + return m_mgr.maybe_get_sarif_source_language (filename); +} + +void +impl_diagnostic_client_data_hooks:: +add_sarif_invocation_properties (sarif_object &) const +{ + // No-op. +} + +/* class sink. */ + +void +sink::emit (diagnostic &diag, const char *msgid, va_list *args) +{ + diagnostic_info info; + diagnostic_set_info (&info, msgid, args, diag.get_rich_location (), + diagnostic_t_from_diagnostic_level (diag.get_level ())); + info.metadata = diag.get_metadata (); + diagnostic_report_diagnostic (&m_dc, &info); +} + +sink::sink (diagnostic_manager &mgr) +: m_mgr (mgr) +{ + diagnostic_initialize (&m_dc, 0); + m_dc.m_client_aux_data = this; + m_dc.m_option_name = get_option_name_cb; + m_dc.set_client_data_hooks (new impl_diagnostic_client_data_hooks (mgr)); + m_dc.file_cache_init (); +} + +sink::~sink () +{ + diagnostic_finish (&m_dc); +} + +/* class text_sink : public sink. */ + +text_sink::text_sink (diagnostic_manager &mgr, + FILE *dst_stream, + enum diagnostic_colorize colorize) +: sink (mgr) +{ + m_dc.set_show_cwe (true); + m_dc.set_show_rules (true); + + diagnostic_color_rule_t color_rule; + switch (colorize) + { + default: + gcc_unreachable (); + case DIAGNOSTIC_COLORIZE_IF_TTY: + color_rule = DIAGNOSTICS_COLOR_AUTO; + break; + case DIAGNOSTIC_COLORIZE_NO: + color_rule = DIAGNOSTICS_COLOR_NO; + break; + case DIAGNOSTIC_COLORIZE_YES: + color_rule = DIAGNOSTICS_COLOR_YES; + break; + } + diagnostic_color_init (&m_dc, color_rule); + m_dc.printer->buffer->stream = dst_stream; + m_dc.m_text_callbacks.begin_diagnostic + = [] (diagnostic_context *context, + diagnostic_info *info) + { + text_sink *sink = static_cast (context->m_client_aux_data); + sink->on_begin_text_diagnostic (info); + }; + m_dc.m_source_printing.enabled = true; // FIXME + m_dc.m_source_printing.colorize_source_p = true; // FIXME + m_dc.m_source_printing.show_labels_p = true; // FIXME + m_dc.m_source_printing.show_line_numbers_p = true; // FIXME + m_dc.m_source_printing.min_margin_width = 6; // FIXME +} + +void +text_sink::on_begin_text_diagnostic (diagnostic_info *info) +{ + const diagnostic *diag = m_mgr.get_current_diag (); + gcc_assert (diag); + const diagnostic_logical_location *diag_logical_loc + = diag->get_logical_location (); + if (m_current_logical_loc != diag_logical_loc) + { + m_current_logical_loc = diag_logical_loc; + if (m_current_logical_loc) + { + char *old_prefix = pp_take_prefix (m_dc.printer); + switch (m_current_logical_loc->get_kind ()) + { + default: + break; + case LOGICAL_LOCATION_KIND_FUNCTION: + if (const char *name + = m_current_logical_loc->get_name_with_scope ()) + { + pp_printf (m_dc.printer, _("In function %qs"), name); + pp_character (m_dc.printer, ':'); + pp_newline (m_dc.printer); + } + break; + // FIXME: handle other cases + } + m_dc.printer->prefix = old_prefix; + } + } + pp_set_prefix (m_dc.printer, + diagnostic_build_prefix (&m_dc, info)); +} + +/* class sarif_sink : public sink. */ + +sarif_sink::sarif_sink (diagnostic_manager &mgr, + FILE *dst_stream, + enum diagnostic_sarif_version) +: sink (mgr) +{ + diagnostic_output_format_init_sarif_stream (&m_dc, dst_stream); +} + +/* struct diagnostic_manager. */ + +void +diagnostic_manager::write_patch (FILE *dst_stream) +{ + pretty_printer pp; + pp.buffer->stream = dst_stream; + m_edit_context.print_diff (&pp, true); + pp_flush (&pp); +} + +void +diagnostic_manager::emit (diagnostic &diag, const char *msgid, va_list *args) +{ + set_line_table_global (); + + m_current_diag = &diag; + for (auto &sink : m_sinks) + { + va_list arg_copy; + va_copy (arg_copy, *args); + sink->emit (diag, msgid, &arg_copy); + } + + rich_location *rich_loc = diag.get_rich_location (); + if (rich_loc->fixits_can_be_auto_applied_p ()) + m_edit_context.add_fixits (rich_loc); + + m_current_diag = nullptr; +} + +/* Public entrypoints. */ + +/* Public entrypoint for clients to acquire a diagnostic_manager. */ + +diagnostic_manager * +diagnostic_manager_new (void) +{ + return new diagnostic_manager (); +} + +/* Public entrypoint for clients to release a diagnostic_manager. */ + +void +diagnostic_manager_release (diagnostic_manager *diag_mgr) +{ + delete diag_mgr; +} + +/* Public entrypoint. */ + +void +diagnostic_manager_set_tool_name (diagnostic_manager *diag_mgr, + const char *value) +{ + gcc_assert (diag_mgr); + gcc_assert (value); + + diag_mgr->get_client_version_info ()->m_name.set (value); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_set_full_name (diagnostic_manager *diag_mgr, + const char *value) +{ + gcc_assert (diag_mgr); + gcc_assert (value); + + diag_mgr->get_client_version_info ()->m_full_name.set (value); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_set_version_string (diagnostic_manager *diag_mgr, + const char *value) +{ + gcc_assert (diag_mgr); + gcc_assert (value); + + diag_mgr->get_client_version_info ()->m_version.set (value); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_set_version_url (diagnostic_manager *diag_mgr, + const char *value) +{ + gcc_assert (diag_mgr); + gcc_assert (value); + + diag_mgr->get_client_version_info ()->m_version_url.set (value); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_add_text_sink (diagnostic_manager *diag_mgr, + FILE *dst_stream, + enum diagnostic_colorize colorize) +{ + gcc_assert (diag_mgr); + gcc_assert (dst_stream); + + diag_mgr->add_sink (make_unique (*diag_mgr, dst_stream, colorize)); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_add_sarif_sink (diagnostic_manager *diag_mgr, + FILE *dst_stream, + enum diagnostic_sarif_version version) +{ + gcc_assert (diag_mgr); + gcc_assert (dst_stream); + + diag_mgr->add_sink (make_unique (*diag_mgr, dst_stream, version)); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_write_patch (diagnostic_manager *diag_mgr, + FILE *dst_stream) +{ + gcc_assert (diag_mgr); + gcc_assert (dst_stream); + + diag_mgr->write_patch (dst_stream); +} + +/* Public entrypoint. */ + +const diagnostic_file * +diagnostic_manager_new_file (diagnostic_manager *diag_mgr, + const char *name, + const char *sarif_source_language) +{ + gcc_assert (diag_mgr); + gcc_assert (name); + + return diag_mgr->new_file (name, sarif_source_language); +} + +/* Public entrypoint. */ + +diagnostic_location_t +diagnostic_manager_new_location_from_file_and_line (diagnostic_manager *diag_mgr, + const diagnostic_file *file, + diagnostic_line_num_t linenum) +{ + gcc_assert (diag_mgr); + gcc_assert (file); + + return diag_mgr->new_location_from_file_and_line (file, linenum); +} + +/* Public entrypoint. */ + +diagnostic_location_t +diagnostic_manager_new_location_from_file_line_column (diagnostic_manager *diag_mgr, + const diagnostic_file *file, + diagnostic_line_num_t line_num, + diagnostic_column_num_t column_num) +{ + gcc_assert (diag_mgr); + gcc_assert (file); + + return diag_mgr->new_location_from_file_line_column (file, + line_num, + column_num); +} + +/* Public entrypoint. */ + +diagnostic_location_t +diagnostic_manager_new_location_from_range (diagnostic_manager *diag_mgr, + diagnostic_location_t loc_caret, + diagnostic_location_t loc_start, + diagnostic_location_t loc_end) +{ + gcc_assert (diag_mgr); + + return diag_mgr->new_location_from_range (loc_caret, + loc_start, + loc_end); +} + + +// FIXME: emit text to stderr + +#if 0 +void +diagnostic_location_debug (diagnostic_manager *diag_mgr, + diagnostic_location_t loc, + FILE *stream) +{ + text_sink sink (stream); + diagnostic d (*diag_mgr, DIAGNOSTIC_LEVEL_NOTE); +} +#endif + +/* Public entrypoint. */ + +const diagnostic_logical_location * +diagnostic_manager_new_logical_location (diagnostic_manager *diag_mgr, + enum diagnostic_logical_location_kind_t kind, + const diagnostic_logical_location *parent, + const char *short_name, + const char *fully_qualified_name, + const char *decorated_name) +{ + gcc_assert (diag_mgr); + + return diag_mgr->new_logical_location (kind, + parent, + short_name, + fully_qualified_name, + decorated_name); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_begin_group (diagnostic_manager *diag_mgr) +{ + gcc_assert (diag_mgr); + diag_mgr->begin_group (); +} + +/* Public entrypoint. */ + +extern void +diagnostic_manager_end_group (diagnostic_manager *diag_mgr) +{ + gcc_assert (diag_mgr); + diag_mgr->end_group (); +} + +/* Public entrypoint. */ + +diagnostic * +diagnostic_begin (diagnostic_manager *diag_mgr, + enum diagnostic_level level) +{ + gcc_assert (diag_mgr); + + return new diagnostic (*diag_mgr, level); +} + +/* Public entrypoint. */ + +void +diagnostic_set_cwe (diagnostic *diag, + unsigned cwe_id) +{ + gcc_assert (diag); + + diag->set_cwe (cwe_id); +} + +/* Public entrypoint. */ + +void +diagnostic_add_rule (diagnostic *diag, + const char *title, + const char *url) +{ + gcc_assert (diag); + + diag->add_rule (title, url); +} + +/* Public entrypoint. */ + +void +diagnostic_set_location (diagnostic *diag, + diagnostic_location_t loc) +{ + gcc_assert (diag); + diag->get_manager ().assert_valid_diagnostic_location_t (loc); + + diag->set_location (loc); +} + +/* Public entrypoint. */ + +void +diagnostic_add_location (diagnostic *diag, + diagnostic_location_t loc) +{ + gcc_assert (diag); + diag->get_manager ().assert_valid_diagnostic_location_t (loc); + + diag->add_location (loc); +} + +/* Public entrypoint. */ + +void +diagnostic_add_location_with_label (diagnostic *diag, + diagnostic_location_t loc, + const char *text) +{ + gcc_assert (diag); + diag->get_manager ().assert_valid_diagnostic_location_t (loc); + gcc_assert (text); + + diag->add_location_with_label (loc, text); +} + +/* Public entrypoint. */ + +void +diagnostic_set_logical_location (diagnostic *diag, + const diagnostic_logical_location *logical_loc) +{ + gcc_assert (diag); + gcc_assert (logical_loc); + + diag->set_logical_location (logical_loc); +} + +/* Public entrypoint. */ + +void +diagnostic_add_fix_it_hint_insert_before (diagnostic *diag, + diagnostic_location_t loc, + const char *addition) +{ + gcc_assert (diag); + diag->get_manager ().assert_valid_diagnostic_location_t (loc); + gcc_assert (addition); + + diag->get_manager ().set_line_table_global (); + diag->get_rich_location ()->add_fixit_insert_before (loc, addition); +} + +/* Public entrypoint. */ + +void +diagnostic_add_fix_it_hint_insert_after (diagnostic *diag, + diagnostic_location_t loc, + const char *addition) +{ + gcc_assert (diag); + diag->get_manager ().assert_valid_diagnostic_location_t (loc); + gcc_assert (addition); + + diag->get_manager ().set_line_table_global (); + diag->get_rich_location ()->add_fixit_insert_after (loc, addition); +} + +/* Public entrypoint. */ + +void +diagnostic_add_fix_it_hint_replace (diagnostic *diag, + diagnostic_location_t loc, + const char *replacement) +{ + gcc_assert (diag); + diag->get_manager ().assert_valid_diagnostic_location_t (loc); + gcc_assert (replacement); + + diag->get_manager ().set_line_table_global (); + diag->get_rich_location ()->add_fixit_replace (loc, replacement); +} + +/* Public entrypoint. */ + +void +diagnostic_add_fix_it_hint_delete (diagnostic *diag, + diagnostic_location_t loc) +{ + gcc_assert (diag); + diag->get_manager ().assert_valid_diagnostic_location_t (loc); + + diag->get_manager ().set_line_table_global (); + diag->get_rich_location ()->add_fixit_remove (loc); +} + +/* Public entrypoint. */ + +void +diagnostic_finish (diagnostic *diag, const char *gmsgid, ...) +{ + gcc_assert (diag); + + if (const char *tool_name + = diag->get_manager ().get_client_version_info ()->m_name.get_str ()) + progname = tool_name; + else + progname = "progname"; + auto_diagnostic_group d; + va_list args; + va_start (args, gmsgid); + gcc_assert (diag); + diag->get_manager ().emit (*diag, gmsgid, &args); + va_end (args); + delete diag; +} diff --git a/gcc/libdiagnostics.map b/gcc/libdiagnostics.map new file mode 100644 index 00000000000..06da1d5c9c2 --- /dev/null +++ b/gcc/libdiagnostics.map @@ -0,0 +1,57 @@ +# Linker script for libdiagnostics.so +# Copyright (C) 2023 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 +# . */ + +# The initial release of the library. +LIBDIAGNOSTICS_ABI_0 +{ + global: + # Keep this list in order of decls in header file. + diagnostic_manager_new; + diagnostic_manager_release; + diagnostic_manager_set_tool_name; + diagnostic_manager_set_full_name; + diagnostic_manager_set_version_string; + diagnostic_manager_set_version_url; + diagnostic_manager_add_text_sink; + diagnostic_manager_add_sarif_sink; + diagnostic_manager_write_patch; + diagnostic_manager_new_file; + diagnostic_manager_new_location_from_file_and_line; + diagnostic_manager_new_location_from_file_line_column; + diagnostic_manager_new_location_from_range; + diagnostic_manager_new_logical_location; + diagnostic_manager_begin_group; + diagnostic_manager_end_group; + diagnostic_begin; + diagnostic_set_cwe; + diagnostic_add_rule; + diagnostic_set_location; + diagnostic_set_location_with_label; + diagnostic_add_location; + diagnostic_add_location_with_label; + diagnostic_set_logical_location; + diagnostic_add_fix_it_hint_insert_before; + diagnostic_add_fix_it_hint_insert_after; + diagnostic_add_fix_it_hint_replace; + diagnostic_add_fix_it_hint_delete; + diagnostic_finish; + + local: *; +}; diff --git a/gcc/testsuite/libdiagnostics.dg/libdiagnostics.exp b/gcc/testsuite/libdiagnostics.dg/libdiagnostics.exp new file mode 100644 index 00000000000..bd50a5568a7 --- /dev/null +++ b/gcc/testsuite/libdiagnostics.dg/libdiagnostics.exp @@ -0,0 +1,544 @@ +# Test code for libdiagnostics.so +# +# FIXME: +# +# Test code for libdiagnostics.so +# We will compile each of jit.dg/test-*.c into an executable +# dynamically linked against libgccjit.so, and then run each +# such executable. +# +# These executables call into the libgccjit.so API to create +# code, compile it, and run it, verifying that the results +# are as expected. See harness.h for shared code used by all +# such executables. +# +# The executables call into DejaGnu's unit testing C API to +# report PASS/FAIL results, which this script gathers back +# up into the Tcl world, reporting a summary of all results +# across all of the executables. + +# Kludge alert: +# We need g++_init so that it can find the stdlib include path. +# +# g++_init (in lib/g++.exp) uses g++_maybe_build_wrapper, +# which normally comes from the definition of +# ${tool}_maybe_build_wrapper within lib/wrapper.exp. +# +# However, for us, ${tool} is "jit". +# Hence we load wrapper.exp with tool == "g++", so that +# g++_maybe_build_wrapper is defined. +set tool g++ +load_lib wrapper.exp +set tool diagnostics + +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 target-supports-dg.exp + +# # Skip these tests for targets that don't support -ldiagnostics +# if { ![check_effective_target_ldiagnostics] } { +# return +# } + +# The default do-what keyword. +set dg-do-what-default compile + +# Look for lines of the form: +# definitely lost: 11,316 bytes in 235 blocks +# indirectly lost: 352 bytes in 4 blocks +# Ideally these would report zero bytes lost (which is a PASS); +# for now, report non-zero leaks as XFAILs. +proc report_leak {kind name logfile line} { + set match [regexp "$kind lost: .*" $line result] + if $match { + verbose "Saw \"$result\" within \"$line\"" 4 + # Extract bytes and blocks. + # These can contain commas as well as numerals, + # but we only care about whether we have zero. + regexp "$kind lost: (.+) bytes in (.+) blocks" \ + $result -> bytes blocks + verbose "bytes: '$bytes'" 4 + verbose "blocks: '$blocks'" 4 + if { $bytes == 0 } { + pass "$name: $logfile: $result" + } else { + xfail "$name: $logfile: $result" + } + } +} + +proc parse_valgrind_logfile {name logfile} { + verbose "parse_valgrind_logfile: $logfile" 2 + if [catch {set f [open $logfile]}] { + fail "$name: unable to read $logfile" + return + } + + while { [gets $f line] >= 0 } { + # Strip off the PID prefix e.g. ==7675== + set line [regsub "==\[0-9\]*== " $line ""] + verbose $line 2 + + report_leak "definitely" $name $logfile $line + report_leak "indirectly" $name $logfile $line + } + close $f +} + +# Given WRES, the result from "wait", issue a PASS +# if the spawnee exited cleanly, or a FAIL for various kinds of +# unexpected exits. + +proc verify_exit_status { executable wres } { + lassign $wres pid spawnid os_error_flag value + verbose "pid: $pid" 3 + verbose "spawnid: $spawnid" 3 + verbose "os_error_flag: $os_error_flag" 3 + verbose "value: $value" 3 + + # Detect segfaults etc: + if { [llength $wres] > 4 } { + if { [lindex $wres 4] == "CHILDKILLED" } { + fail "$executable killed: $wres" + return + } + } + if { $os_error_flag != 0 } { + fail "$executable: OS error: $wres" + return + } + if { $value != 0 } { + fail "$executable: non-zero exit code: $wres" + return + } + pass "$executable exited cleanly" +} + +# This is host_execute from dejagnu.exp commit +# 126a089777158a7891ff975473939f08c0e31a1c +# with the following patch applied, and renaming to "fixed_host_execute". +# See the discussion at +# http://lists.gnu.org/archive/html/dejagnu/2014-10/msg00000.html +# +# --- /usr/share/dejagnu/dejagnu.exp.old 2014-10-08 13:38:57.274068541 -0400 +# +++ /usr/share/dejagnu/dejagnu.exp 2014-10-10 12:27:51.113813659 -0400 +# @@ -113,8 +113,6 @@ proc host_execute {args} { +# set timetol 0 +# set arguments "" +# +# - expect_before buffer_full { perror "Buffer full" } +# - +# if { [llength $args] == 0} { +# set executable $args +# } else { + + +# Execute the executable file, and anaylyse the output for the +# test state keywords. +# Returns: +# A "" (empty) string if everything worked, or an error message +# if there was a problem. +# +proc fixed_host_execute {args} { + global env + global text + global spawn_id + + verbose "fixed_host_execute: $args" + + set timeoutmsg "Timed out: Never got started, " + set timeout 100 + set file all + set timetol 0 + set arguments "" + + if { [llength $args] == 0} { + set executable $args + } else { + set executable [lindex $args 0] + set params [lindex $args 1] + } + + verbose "The executable is $executable" 2 + if {![file exists ${executable}]} { + perror "The executable, \"$executable\" is missing" 0 + return "No source file found" + } elseif {![file executable ${executable}]} { + perror "The executable, \"$executable\" is not usable" 0 + return "Bad executable found" + } + + verbose "params: $params" 2 + + # spawn the executable and look for the DejaGnu output messages from the + # test case. + # spawn -noecho -open [open "|./${executable}" "r"] + + # Run under valgrind if RUN_UNDER_VALGRIND is present in the environment. + # Note that it's best to configure gcc with --enable-valgrind-annotations + # when testing under valgrind. + set run_under_valgrind [info exists env(RUN_UNDER_VALGRIND)] + if $run_under_valgrind { + set valgrind_logfile "${executable}.valgrind.txt" + set valgrind_params {"valgrind"} + lappend valgrind_params "--leak-check=full" + lappend valgrind_params "--log-file=${valgrind_logfile}" + } else { + set valgrind_params {} + } + verbose "valgrind_params: $valgrind_params" 2 + + set args ${valgrind_params} + lappend args "./${executable}" + set args [concat $args ${params}] + verbose "args: $args" 2 + + # We checked that the executable exists above, and can be executed, but + # that does not cover other reasons that the launch could fail (e.g. + # missing or malformed params); catch such cases here and report them. + set err [catch "spawn -noecho $args" pid] + set sub_proc_id $spawn_id + if { $pid <= 0 || $err != 0 || $sub_proc_id < 0 } { + warning "failed to spawn : $args : err = $err" + } + + # Increase the buffer size, if needed to avoid spurious buffer-full + # events; GCC uses 10000; chose a power of two here. + set current_max_match [match_max -i $sub_proc_id] + if { $current_max_match < 8192 } { + match_max -i $sub_proc_id 8192 + set used [match_max -i $sub_proc_id] + } + + # If we get a buffer-full error, that seems to be unrecoverable so try to + # exit in a reasonable manner to avoid wedged processes. + expect_after full_buffer { + verbose -log "fixed_host_execute: $args FULL BUFFER" + # FIXME: this all assumes that closing the connection will cause the + # sub-process to terminate (but that is not going to be the case if, + # for example, there is something started with -nohup somewhere). + # We should explicitly kill it here. + # Set the process to be a nowait exit. + wait -nowait -i $sub_proc_id + catch close + perror "${executable} got full_buffer" + return "${executable} got full_buffer" + } + + set prefix "\[^\r\n\]*" + # Work around a Darwin tcl or termios bug that sometimes inserts extra + # CR characters into the cooked tty stream + set endline "\r\n" + if { [istarget *-*-darwin*] } { + set endline "\r(\r)*\n" + } + + # Note that the logic here assumes that we cannot (validly) get single + # carriage return or line feed characters in the stream. If those occur, + # it will stop any further matching. We arange for the matching to be + # at the start of the buffer - so that if there is any spurious output + # to be discarded, it must be done explicitly - not by matching part-way + # through the buffer. + expect { + -re "^$prefix\[0-9\]\[0-9\]:..:..:${text}*$endline" { + regsub "\[\n\r\t\]*NOTE: $text\r\n" $expect_out(0,string) "" output + verbose "$output" 3 + set timetol 0 + exp_continue + } + -re "^\tNOTE: (\[^\r\n\]+)$endline" { + # discard notes. + verbose "Ignored note: $expect_out(1,string)" 2 + set timetol 0 + exp_continue + } + -re "^\tPASSED: (\[^\r\n\]+)$endline" { + pass "$expect_out(1,string)" + set timetol 0 + exp_continue + } + -re "^\tFAILED: (\[^\r\n\]+)$endline" { + fail "$expect_out(1,string)" + set timetol 0 + exp_continue + } + -re "^\tUNTESTED: (\[^\r\n\]+)$endline" { + untested "$expect_out(1,string)" + set timetol 0 + exp_continue + } + -re "^\tUNRESOLVED: (\[^\r\n\]+)$endline" { + unresolved "$expect_out(1,string)" + set timetol 0 + exp_continue + } + -re "^$prefix$endline" { + # This matches and discards any other lines (including blank ones). + if { [string length $expect_out(buffer)] <= 2 } { + set output "blank line" + } else { + set output [string range $expect_out(buffer) 0 end-2] + } + verbose -log "DISCARDED $expect_out(spawn_id) : $output" + exp_continue + } + eof { + # This seems to be the only way that we can reliably know that the + # output is finished since there are cases where further output + # follows the dejagnu test harness totals. + verbose "saw eof" 2 + } + timeout { + if { $timetol <= 2 } { + verbose -log "Timed out with retry (timeout = $timeout)" + incr timetol + exp_continue + } else { + warning "Timed out executing testcase (timeout = $timeout)" + catch close + return "Timed out executing test case" + } + } + } + + # Use "wait" to pick up the sub-process exit state. If the sub-process is + # writing to a file (perhaps under valgrind) then that also needs to be + # complete; only attempt this on a valid spawn. + if { $sub_proc_id > 0 } { + verbose "waiting for $sub_proc_id" 1 + # Be explicit about what we are waiting for. + catch "wait -i $sub_proc_id" wres + verbose "wres: $wres" 2 + verify_exit_status $executable $wres + } + + if $run_under_valgrind { + upvar 2 name name + parse_valgrind_logfile $name $valgrind_logfile + } + + # force a close of the executable to be safe. + catch close + + return "" +} + +# (end of code from dejagnu.exp) + +# GCC_UNDER_TEST is needed by gcc_target_compile +global GCC_UNDER_TEST +if ![info exists GCC_UNDER_TEST] { + set GCC_UNDER_TEST "[find_gcc]" +} + +g++_init + +# Initialize dg. +dg-init + +# Gather a list of all tests. + +# C tests within the testsuite: gcc/testsuite/libdiagnostics.dg/test-*.c +set tests [find $srcdir/$subdir test-*.c] + +set tests [lsort $tests] + +verbose "tests: $tests" + +# libgloss has found the driver (as "xgcc" or "gcc) and stored +# its full path as GCC_UNDER_TEST. +proc get_path_of_driver {} { + global GCC_UNDER_TEST + + verbose "GCC_UNDER_TEST: $GCC_UNDER_TEST" + set binary [lindex $GCC_UNDER_TEST 0] + verbose "binary: $binary" + + return [file dirname $binary] +} + +# Expand "SRCDIR" within ARG to the location of the top-level +# src directory + +proc diagnostics-expand-vars {arg} { + verbose "diagnostics-expand-vars: $arg" + global srcdir + verbose " srcdir: $srcdir" + # "srcdir" is that of the gcc/testsuite directory, so + # we need to go up two levels. + set arg [string map [list "SRCDIR" $srcdir/../..] $arg] + verbose " new arg: $arg" + return $arg +} + +# Parameters used when invoking the executables built from the test cases. + +global diagnostics-exe-params +set diagnostics-exe-params {} + +# Set "diagnostics-exe-params", expanding "SRCDIR" in each arg to the location of +# the top-level srcdir. + +proc dg-diagnostics-set-exe-params { args } { + verbose "dg-diagnostics-set-exe-params: $args" + + global diagnostics-exe-params + set diagnostics-exe-params {} + # Skip initial arg (line number) + foreach arg [lrange $args 1 [llength $args] ] { + lappend diagnostics-exe-params [diagnostics-expand-vars $arg] + } +} + +proc diagnostics-dg-test { prog do_what extra_tool_flags } { + verbose "within diagnostics-dg-test..." + verbose " prog: $prog" + verbose " do_what: $do_what" + verbose " extra_tool_flags: $extra_tool_flags" + + global dg-do-what-default + set dg-do-what [list ${dg-do-what-default} "" P] + + set tmp [dg-get-options $prog] + foreach op $tmp { + verbose "Processing option: $op" 3 + set status [catch "$op" errmsg] + if { $status != 0 } { + if { 0 && [info exists errorInfo] } { + # This also prints a backtrace which will just confuse + # testcase writers, so it's disabled. + perror "$name: $errorInfo\n" + } else { + perror "$name: $errmsg for \"$op\"\n" + } + perror "$name: $errmsg for \"$op\"" 0 + return + } + } + + # If we're not supposed to try this test on this target, we're done. + if { [lindex ${dg-do-what} 1] == "N" } { + unsupported "$name" + verbose "$name not supported on this target, skipping it" 3 + return + } + + # Determine what to name the built executable. + # + # We simply append .exe to the filename, e.g. + # "test-foo.c.exe" + # since some testcases exist in both + # "test-foo.c" and + # "test-foo.cc" + # variants, and we don't want them to clobber each other's + # executables. + # + # This also ensures that the source name makes it into the + # pass/fail output, so that we can distinguish e.g. which test-foo + # is failing. + set output_file "[file tail $prog].exe" + verbose "output_file: $output_file" + + # Create the test executable: + set extension [file extension $prog] + if {$extension == ".cc"} { + set compilation_function "g++_target_compile" + set options "{additional_flags=$extra_tool_flags}" + } else { + set compilation_function "gcc_target_compile" + # Until recently, assumed -fgnu89-inline + # Ideally we should fixincludes it (PR other/63613), but + # for now add -fgnu89-inline when compiling C JIT testcases. + # See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63613 + # and http://lists.gnu.org/archive/html/dejagnu/2014-10/msg00011.html + set options "{additional_flags=$extra_tool_flags -fgnu89-inline}" + } + verbose "compilation_function=$compilation_function" + verbose "options=$options" + + set comp_output [$compilation_function $prog $output_file \ + "executable" $options] + upvar 1 name name + if ![diagnostics_check_compile "$name" "initial compilation" \ + $output_file $comp_output] then { + return + } + + # Run the test executable, capturing the PASS/FAIL textual output + # from the C API, converting it into the Tcl API. + + # We need to set LD_LIBRARY_PATH so that the test files can find + # libdiagnostics.so + # Do this using set_ld_library_path_env_vars from target-libpath.exp + # We will restore the old value later using + # restore_ld_library_path_env_vars. + + # Unfortunately this API only supports a single saved value, rather + # than a stack, and g++_init has already called into this API, + # injecting the appropriate value for LD_LIBRARY_PATH for finding + # the built copy of libstdc++. + # Hence the call to restore_ld_library_path_env_vars would restore + # the *initial* value of LD_LIBRARY_PATH, and attempts to run + # a C++ testcase after running any prior testcases would thus look + # in the wrong place for libstdc++. This led to failures at startup + # of the form: + # ./tut01-hello-world.cc.exe: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by ./tut01-hello-world.cc.exe) + # when the built libstdc++ is more recent that the system libstdc++. + # + # As a workaround, reset the variable "orig_environment_saved" within + # target-libpath.exp, so that the {set|restore}_ld_library_path_env_vars + # API saves/restores the current value of LD_LIBRARY_PATH (as set up + # by g++_init). + global orig_environment_saved + set orig_environment_saved 0 + + global ld_library_path + global base_dir + set ld_library_path "$base_dir/../../" + set_ld_library_path_env_vars + + # dejagnu.exp's host_execute has code to scrape out test results + # from the DejaGnu C API and bring back into the tcl world, so we + # use that to invoke the built code. + # However, it appears to be buggy; see: + # http://lists.gnu.org/archive/html/dejagnu/2014-10/msg00000.html + # We instead call a patched local copy, "fixed_host_execute", defined + # above. + + global diagnostics-exe-params + set args ${diagnostics-exe-params} + set diagnostics-exe-params {} + + set result [fixed_host_execute $output_file $args ] + verbose "result: $result" + + restore_ld_library_path_env_vars + + # Normally we would return $comp_output and $output_file to the + # caller, which would delete $output_file, the generated executable. + # If we need to debug, it's handy to be able to suppress this behavior, + # keeping the executable around. + + global env + set preserve_executables [info exists env(PRESERVE_EXECUTABLES)] + if $preserve_executables { + set output_file "" + } + + return [list $comp_output $output_file] +} + +set DEFAULT_CFLAGS "-I$srcdir/.. -ldiagnostics -g -Wall -Werror" + +# Main loop. This will invoke jig-dg-test on each test-*.c file. +dg-runtest $tests "" $DEFAULT_CFLAGS + +# All done. +dg-finish From patchwork Tue Nov 21 22:20:18 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 168005 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:2b07:b0:403:3b70:6f57 with SMTP id io7csp949829vqb; Tue, 21 Nov 2023 14:21:12 -0800 (PST) X-Google-Smtp-Source: AGHT+IHj03VCKtXrd+bCPiGh6iuimgbe1Y5kmAZB17vPQUpgDQbLrLeiI4y4mQhF5+zZZTKNSaOE X-Received: by 2002:a05:620a:2812:b0:77d:5751:51c8 with SMTP id f18-20020a05620a281200b0077d575151c8mr448508qkp.77.1700605272321; Tue, 21 Nov 2023 14:21:12 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1700605272; cv=pass; d=google.com; s=arc-20160816; b=RBlfOIXtx9vCn9HAi9cmk3w9bhmSWoiPh7Ki7NFCGpp4utWewXEYNWlHsNJc6cwC0X Bs0wfxNBnenffbr9KhAdpTWwTdob0mZ3ZtA2OMTPIxjk+wymXMCQehuVCyJK2hC8iD88 EXXFsv0VThlpT5tjypwRqimP2ymfPZv3L4KbVxs7K3NqnZHK2+oV0YutgcAQNEZCOAem /QMQNH3zDWRKHvVIhes3ZTBUX0Be89xQnY9oPggwo2Iv9CMZ+OXYjs0xZ86NYYM4VcV/ kH/XG+95VSo+Czu1WemK4QC3PzyOUXo/Lg0Q9xyVqUMF8YP32e/HJE3+g/g4Nsdesisw LG6g== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature:arc-filter:dmarc-filter:delivered-to; bh=Uvvlossdf8boUbECl6hyDbubQLkAqPxICLA3Tz+TLPs=; fh=rfR2SIXf27jLXVYT4RHMSVZgLIsrp2E1IHKtUM1Ws70=; b=EjBb8ebDFRNq4jRwIVTuWRkAxu78ZJWOM2OF3b8ShxhRqYLCeDfFCAkrodz8aPibIX nmCfuaC4aP9VtdqKQFTYaZ7/qsnhy5n2P2NAfqWTNgq5YuCwwFdphjxA9p9LlLzF2X3d 5j6JgDlmctiq8rQSlKmpu+lGfx9Z5vMs94txxg9KRSf2c9u/phxEhXR05ebpEA1Zj/f8 x8Z4VxA7IA3TIjtnP/r/mORARRELtQ6oBSsf3amefz+zVqexTmZrMce9oCvz8wri3wGy lPdiN0AGEY4vKcV9uL1p2x4/2ebFAqY5xt9jl/erIBxyrEZWqjBb0VGC/cgT1bpOcj40 bYVA== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=b6NIsAYA; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id i26-20020ae9ee1a000000b0076ceb5934f6si9645509qkg.64.2023.11.21.14.21.12 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 21 Nov 2023 14:21:12 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=b6NIsAYA; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id DCBDE3857719 for ; Tue, 21 Nov 2023 22:20:54 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org 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 E2A3A38582B8 for ; Tue, 21 Nov 2023 22:20:24 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E2A3A38582B8 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org E2A3A38582B8 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700605226; cv=none; b=nZ9EK1CU8ItPqrwfUV+/NG+41TnXehilqRuQaPFUN5TW4984LJoVEKMGd6YxHHEuuCffYoiiji48Ao3ZhP1oISrlzXFwmGOaRc8gmB6e4HvKfKsMqjC1jKTwkh/Ublf9TbHJWM1fxW06XFpwh1KlAp58Umh3duvahxJZqTK4v98= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700605226; c=relaxed/simple; bh=vRM4dvP9BEVRIESxsNrac/KBdtw0acKZ9BNr3A864Qc=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=TIcrbdTWCqSqmGU6nBJ0dljRxTq63ad+CB+EkC9S5gioMOcdYFwAW9h+PSN87GTVv6ljE/vUyH8EVx9RWqX3dJFM7mX7JP0u7TbnN2n6RXr/KH3r2dqcihn7pIihAa+MqdLeQPPUfxDxo7V91h/fccUgGkjoATDIPV02FYkqNOg= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1700605224; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Uvvlossdf8boUbECl6hyDbubQLkAqPxICLA3Tz+TLPs=; b=b6NIsAYApSo7cfa5RIyMFSVIkZTGWX4LoOPDrtzs155aKM4q+dAaDWIbxqHBfjeXu5hrJL /DvqDLdR95hpgYQgT/mdpYR98Xa9NYRc7QjTk1ZyQpDsWb6IHayQOMNb/sc2a+RvntTN1J Jo1hAsB8qzCyFPoeweawDIEUOEfICB0= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-677-HVA08a4NNl24GNwwmn-EhQ-1; Tue, 21 Nov 2023 17:20:23 -0500 X-MC-Unique: HVA08a4NNl24GNwwmn-EhQ-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 3B5C93812581; Tue, 21 Nov 2023 22:20:23 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.22.10.115]) by smtp.corp.redhat.com (Postfix) with ESMTP id EC60B492BFA; Tue, 21 Nov 2023 22:20:22 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org, binutils@sourceware.org Cc: Nick Clifton , Simon Sobisch , David Malcolm Subject: [PATCH 5/5] diagnostics: don't print annotation lines when there's no column info Date: Tue, 21 Nov 2023 17:20:18 -0500 Message-Id: <20231121222019.646253-6-dmalcolm@redhat.com> In-Reply-To: <20231121222019.646253-1-dmalcolm@redhat.com> References: <20231106222959.2707741-1-dmalcolm@redhat.com> <20231121222019.646253-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1783213873978542068 X-GMAIL-MSGID: 1783213873978542068 gcc/ChangeLog: * diagnostic-show-locus.cc (layout::maybe_add_location_range): Don't print annotation lines for ranges when there's no column info. (selftest::test_one_liner_no_column): New. (selftest::test_diagnostic_show_locus_one_liner): Call it. --- gcc/diagnostic-show-locus.cc | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/gcc/diagnostic-show-locus.cc b/gcc/diagnostic-show-locus.cc index 563d2826f249..55e7166b9448 100644 --- a/gcc/diagnostic-show-locus.cc +++ b/gcc/diagnostic-show-locus.cc @@ -1295,6 +1295,15 @@ layout::maybe_add_location_range (const location_range *loc_range, sanely relative to the primary location. */ return false; + /* If there's no column information, then don't try to print + annotation lines for this range. */ + enum range_display_kind range_display_kind + = loc_range->m_range_display_kind; + if (start.column == 0 + || finish.column == 0 + || caret.column == 0) + range_display_kind = SHOW_LINES_WITHOUT_RANGE; + /* Everything is now known to be in the correct source file, but it may require further sanitization. */ layout_range ri (exploc_with_display_col (m_file_cache, @@ -1303,7 +1312,7 @@ layout::maybe_add_location_range (const location_range *loc_range, exploc_with_display_col (m_file_cache, finish, m_policy, LOCATION_ASPECT_FINISH), - loc_range->m_range_display_kind, + range_display_kind, exploc_with_display_col (m_file_cache, caret, m_policy, LOCATION_ASPECT_CARET), @@ -3297,6 +3306,20 @@ test_one_liner_simple_caret () pp_formatted_text (dc.printer)); } +/* No column information (column == 0). + No annotation line should be printed. */ + +static void +test_one_liner_no_column () +{ + test_diagnostic_context dc; + location_t caret = linemap_position_for_column (line_table, 0); + rich_location richloc (line_table, caret); + diagnostic_show_locus (&dc, &richloc, DK_ERROR); + ASSERT_STREQ (" foo = bar.field;\n", + pp_formatted_text (dc.printer)); +} + /* Caret and range. */ static void @@ -3848,6 +3871,7 @@ test_diagnostic_show_locus_one_liner (const line_table_case &case_) ASSERT_EQ (16, LOCATION_COLUMN (line_end)); test_one_liner_simple_caret (); + test_one_liner_no_column (); test_one_liner_caret_and_range (); test_one_liner_multiple_carets_and_ranges (); test_one_liner_fixit_insert_before ();