From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 3FEE638582BC for ; Tue, 21 Nov 2023 22:20:25 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 3FEE638582BC 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 3FEE638582BC 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=1700605231; cv=none; b=CKRr0IrSLnZQ6HtLuC9CrTi4gpM0JugUweqJTN5Wt0p0FnaIKpSsvNqM8okIPujrEgbU3wLkcYWDh+xYNu4LvehLlrJOpoqRCZCUt9G9BcdjZoF5J7PTBaFchfwVhtDodUaOsvmkSSMbhauw4UYKTEtlk8vj6zeB8uhJZo9xqng= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700605231; c=relaxed/simple; bh=BvXpNM4Lm11pB/6+MmCinus92Ku17AATeLgYqbi6KLQ=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=UbeahPWNCLimC4PqRzV68wqW+X52V4Ut4iWodec4J773dR2LO0gsMZE/eZF3ujEyQbghr8+YXc0ppfLSmeiZA3zC1DU6N6rhmqpaGvMsCl1ya55X1Bg7cSjAp0KXxBbyGAj53KZtRKTuVquzWfmvonqyRQkaahnjmZjlP5MBahg= 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 Content-Transfer-Encoding: 8bit Content-Type: text/plain; charset="US-ASCII"; x-default=true 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=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: 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; +}; -- 2.26.3