From patchwork Thu Aug 15 18:11:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 1972876 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=b8MP+qad; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4WlCtd4wGlz1yfL for ; Fri, 16 Aug 2024 04:14:01 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id DB615385C6CB for ; Thu, 15 Aug 2024 18:13:59 +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 ESMTP id C10013858C41 for ; Thu, 15 Aug 2024 18:12:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C10013858C41 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 C10013858C41 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=1723745539; cv=none; b=rp795In7X568aQiBkIDCIq7NHTGDvwD2eJgwM4YHUS/AkhxUOJjts9SZmKIqTxwXnBJcnCfH4sCDTDRcRMEmADBhHGI/HuVNRC0O/SkbP3uBEX/Nw2wm4y0hE56T0DvuX95GQq9UOO70uIVaYHj5qHUVNJQkpUw5H0Vz4iTsa7U= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1723745539; c=relaxed/simple; bh=MyJ4snH0fMxcH16PpelLIYdPFHPTLMz/DLiY+6CEHJA=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=WmDzqvye0PvIDxIVOkGEoF2FFCpNqj5+PnJb1N4+U/+bhYlmh9610vtMSdvrGD7sQeE/JxDJ4JTKGpNcRWYGgr41tUAS+cQuOLHIFWTNL+Ssua1jEPAcI6SCzMEq4UZATGM1L9qvzv8kZoF4KWtHe0BGObjdBDK6yT6XlitehA4= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1723745528; 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=Cjg1Ij3hkS/uJao3Ke72szLiVj+cfFVNxfjVwGwFUsw=; b=b8MP+qadXNY59aygOfM9Ele8h8vtbGb2TbwDnnOYx5/BXqViIr3EeQfAaH6mbobgknIGE8 1XGIm+SD7m/QGHBN68Gz9U1yO3/U0MRJ536l87wEIdehuTXX3KQHdPix2/lrUum8U9hEpx V6V/jNwZ7qahm18A5t76WA21E14+UyY= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-597-ToC3wPlzNRClOpqItFQpxw-1; Thu, 15 Aug 2024 14:12:04 -0400 X-MC-Unique: ToC3wPlzNRClOpqItFQpxw-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (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 mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 7D7C31955BF4 for ; Thu, 15 Aug 2024 18:12:03 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.22.16.43]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 26F621955D44; Thu, 15 Aug 2024 18:12:01 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [PATCH 2/7] libdiagnostics v3: implementation Date: Thu, 15 Aug 2024 14:11:51 -0400 Message-Id: <20240815181156.1815075-3-dmalcolm@redhat.com> In-Reply-To: <20240815181156.1815075-1-dmalcolm@redhat.com> References: <20240815181156.1815075-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 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, 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~incoming=patchwork.ozlabs.org@gcc.gnu.org Changed in v3: * Added a --enable-libdiagnostics to configure.ac. It is disabled by default, and requires --enable-host-shared. * Split out gcc/testsuite/libdiagnostics.dg/libdiagnostics.exp into another patch * Update copyright year * class diagnostic_logical_location: Add get_name_for_path_output. * as_diagnostic_event_id: New function. * on_begin_text_diagnostic: Make "info" const. * diagnostic_manager::~diagnostic_manager: Free m_line_table.m_location_adhoc_data_map.data and m_line_table.info_ordinary.maps. * diagnostic_manager::new_location_from_file_and_line: Move code into ensure_linemap_for_file_and_line. * diagnostic_manager::new_location_from_file_line_column: Use ensure_linemap_for_file_and_line rather than always using LC_ENTER. * class libdiagnostics_path_event: New. * class libdiagnostics_path_thread: New. * struct diagnostic_execution_path: New. * diagnostic::diagnostic: Initialize m_path. * diagnostic::add_execution_path: New. * diagnostic::take_execution_path: New. * diagnostic::m_path: New field. * diagnostic_text_sink::diagnostic_text_sink: Initialize m_current_logical_loc. Call diagnostic_urls_init, set_show_cwe, set_show_rules, and set m_show_column. * diagnostic_text_sink::on_begin_text_diagnostic: Various fixes. * sarif_sink::sarif_sink: Add "main_input_file" param. * diagnostic_manager::write_patch: Update for change to pretty_printer. * diagnostic_manager::new_execution_path: New. * diagnostic_manager_add_sarif_sink: Add "main_input_file" param. * diagnostic_manager_debug_dump_location: Call diagnostic_finish. * diagnostic_set_logical_location: Allow logical_loc to be NULL. * diagnostic_add_execution_path: New. * diagnostic_manager_new_execution_path: New. * diagnostic_take_execution_path: New. * diagnostic_execution_path_release: New. * diagnostic_execution_path_add_event: New. * diagnostic_execution_path_add_event_va: New. Changed in v2: * Changed diagnostic_location_t -> const diagnostic_physical_location * * new entrypoint: diagnostic_finish_va * new debugging entrypoints for dumping to a FILE * * Makefile.in: dropped FULL_DRIVER_NAME from libdiagnostics * fix up for my recent changes to gcc/diagnostic.h Blurb from v1: 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. ChangeLog: * configure: Regenerate. * configure.ac: Add --enable-libdiagnostics. gcc/ChangeLog: * Makefile.in (enable_libdiagnostics): New. (lang_checks): If libdiagnostics is enabled, add check-libdiagnostics. (ALL_HOST_OBJS): If libdiagnostics is enabled, add $(libdiagnostics_OBJS). (start.encap): Add LIBDIAGNOSTICS. (libdiagnostics_OBJS): New. (LIBDIAGNOSTICS_VERSION_NUM): New, adapted from code in jit/Make-lang.in. (LIBDIAGNOSTICS_MINOR_NUM): Likewise. (LIBDIAGNOSTICS_RELEASE_NUM): Likewise. (LIBDIAGNOSTICS_FILENAME): Likewise. (LIBDIAGNOSTICS_IMPORT_LIB): Likewise. (libdiagnostics): Likewise. (LIBDIAGNOSTICS_AGE): Likewise. (LIBDIAGNOSTICS_BASENAME): Likewise. (LIBDIAGNOSTICS_SONAME): Likewise. (LIBDIAGNOSTICS_LINKER_NAME): Likewise. (LIBDIAGNOSTICS_COMMA): Likewise. (LIBDIAGNOSTICS_VERSION_SCRIPT_OPTION): Likewise. (LIBDIAGNOSTICS_SONAME_OPTION): Likewise. (LIBDIAGNOSTICS_SONAME_SYMLINK): Likewise. (LIBDIAGNOSTICS_LINKER_NAME_SYMLINK): Likewise. (LIBDIAGNOSTICS_FILENAME): Likewise. (libdiagnostics.serial): Likewise. (LIBDIAGNOSTICS_EXTRA_OPTS): Likewise. (install): If libdiagnostics is enabled, add install-libdiagnostics. (libdiagnostics.install-headers): New. (libdiagnostics.install-common): New, adapted from code in jit/Make-lang.in. (install-libdiagnostics): New. * configure: Regenerate. * configure.ac (check_languages): Add check-libdiagnostics. (--enable-libdiagnostics): New. * diagnostic-event-id.h (diagnostic_event_id_t::zero_based): New. * doc/install.texi (--enable-libdiagnostics): New. * libdiagnostics.cc: New file. * libdiagnostics.map: New file. Signed-off-by: David Malcolm --- configure | 42 + configure.ac | 35 + gcc/Makefile.in | 179 +++- gcc/configure | 26 +- gcc/configure.ac | 16 + gcc/diagnostic-event-id.h | 6 + gcc/doc/install.texi | 7 + gcc/libdiagnostics.cc | 1652 +++++++++++++++++++++++++++++++++++++ gcc/libdiagnostics.map | 72 ++ 9 files changed, 2032 insertions(+), 3 deletions(-) create mode 100644 gcc/libdiagnostics.cc create mode 100644 gcc/libdiagnostics.map diff --git a/configure b/configure index 51bf1d1add18..be6e41439990 100755 --- a/configure +++ b/configure @@ -691,6 +691,7 @@ extra_host_libiberty_configure_flags stage1_languages host_libs_picflag CRAB1_LIBS +enable_libdiagnostics PICFLAG host_shared gcc_host_pie @@ -844,6 +845,7 @@ enable_linker_plugin_configure_flags enable_linker_plugin_flags enable_host_pie enable_host_shared +enable_libdiagnostics enable_stage1_languages enable_objc_gc with_target_bdw_gc @@ -1578,6 +1580,7 @@ Optional Features: plugins [none] --enable-host-pie build position independent host executables --enable-host-shared build host code as shared libraries + --enable-libdiagnostics build libdiagnostics shared library --enable-stage1-languages[=all] choose additional languages to build during stage1. Mostly useful for compiler development @@ -8876,6 +8879,45 @@ fi + +# Check for libdiagnostics support. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable libdiagnostics" >&5 +$as_echo_n "checking whether to enable libdiagnostics... " >&6; } +# Check whether --enable-libdiagnostics was given. +if test "${enable_libdiagnostics+set}" = set; then : + enableval=$enable_libdiagnostics; enable_libdiagnostics=$enableval +else + enable_libdiagnostics=no +fi + + +if test x$enable_libdiagnostics = xyes; then + # Disable libdiagnostics if -enable-host-shared not specified + # but not if building for Mingw. All code in Windows + # is position independent code (PIC). + case $target in + *mingw*) ;; + *) + if test x$host_shared != xyes; then + as_fn_error $? " +Enabling libdiagnostics requires --enable-host-shared. + +--enable-host-shared typically slows the rest of the compiler down by +a few %, so you must explicitly enable it. + +If you want to build both libdiagnostics and the regular compiler, it is often +best to do this via two separate configure/builds, in separate +directories, to avoid imposing the performance cost of +--enable-host-shared on the regular compiler." "$LINENO" 5 + fi + ;; + esac +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_libdiagnostics" >&5 +$as_echo "$enable_libdiagnostics" >&6; } + + + # Rust requires -ldl and -lpthread if you are using an old glibc that does not include them by # default, so we check for them here diff --git a/configure.ac b/configure.ac index 20457005e299..ba2712d6dd52 100644 --- a/configure.ac +++ b/configure.ac @@ -2037,6 +2037,41 @@ fi AC_SUBST(PICFLAG) + +# Check for libdiagnostics support. +AC_MSG_CHECKING([whether to enable libdiagnostics]) +AC_ARG_ENABLE(libdiagnostics, +[AS_HELP_STRING([--enable-libdiagnostics], + [build libdiagnostics shared library])], +enable_libdiagnostics=$enableval, +enable_libdiagnostics=no) + +if test x$enable_libdiagnostics = xyes; then + # Disable libdiagnostics if -enable-host-shared not specified + # but not if building for Mingw. All code in Windows + # is position independent code (PIC). + case $target in + *mingw*) ;; + *) + if test x$host_shared != xyes; then + AC_MSG_ERROR([ +Enabling libdiagnostics requires --enable-host-shared. + +--enable-host-shared typically slows the rest of the compiler down by +a few %, so you must explicitly enable it. + +If you want to build both libdiagnostics and the regular compiler, it is often +best to do this via two separate configure/builds, in separate +directories, to avoid imposing the performance cost of +--enable-host-shared on the regular compiler.]) + fi + ;; + esac +fi +AC_MSG_RESULT($enable_libdiagnostics) +AC_SUBST(enable_libdiagnostics) + + # Rust requires -ldl and -lpthread if you are using an old glibc that does not include them by # default, so we check for them here diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 8fba8f7db6a2..3e4c7bd645f9 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -436,6 +436,8 @@ endif enable_host_shared = @enable_host_shared@ +enable_libdiagnostics = @enable_libdiagnostics@ + enable_as_accelerator = @enable_as_accelerator@ CPPLIB = ../libcpp/libcpp.a @@ -615,6 +617,9 @@ xm_include_list=@xm_include_list@ xm_defines=@xm_defines@ lang_checks= lang_checks_parallelized= +ifeq (@enable_libdiagnostics@,yes) +lang_checks += check-libdiagnostics +endif 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@ lang_tree_files=@lang_tree_files@ @@ -1873,6 +1878,10 @@ endif # compilation or not. ALL_HOST_OBJS = $(ALL_HOST_FRONTEND_OBJS) $(ALL_HOST_BACKEND_OBJS) +ifeq (@enable_libdiagnostics@,yes) +ALL_HOST_OBJS += $(libdiagnostics_OBJS) +endif + BACKEND = libbackend.a main.o libcommon-target.a libcommon.a \ $(CPPLIB) $(LIBDECNUMBER) @@ -2179,7 +2188,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 @@ -2268,6 +2277,129 @@ 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 + +# libdiagnostics + +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) + +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) + +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) + +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 @@ -3808,6 +3940,10 @@ ifeq ($(enable_plugin),yes) install: install-plugin endif +ifeq ($(enable_libdiagnostics),yes) +install: install-libdiagnostics +endif + install-strip: override INSTALL_PROGRAM = $(INSTALL_STRIP_PROGRAM) ifneq ($(STRIP),) install-strip: STRIPPROG = $(STRIP) @@ -3984,6 +4120,47 @@ install-driver: installdirs xgcc$(exeext) fi; \ fi +libdiagnostics.install-headers: installdirs + $(INSTALL_DATA) $(srcdir)/libdiagnostics.h \ + $(DESTDIR)$(includedir)/libdiagnostics.h + $(INSTALL_DATA) $(srcdir)/libdiagnostics++.h \ + $(DESTDIR)$(includedir)/libdiagnostics++.h + +ifneq (,$(findstring mingw,$(target))) +libdiagnostics.install-common: installdirs libdiagnostics.install-headers +# Install import library + $(INSTALL_PROGRAM) $(LIBDIAGNOSTICS_IMPORT_LIB) \ + $(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_IMPORT_LIB) +# Install DLL file + $(INSTALL_PROGRAM) $(LIBDIAGNOSTICS_FILENAME) \ + $(DESTDIR)$(bindir)/$(LIBDIAGNOSTICS_FILENAME) + +else +ifneq (,$(findstring darwin,$(host))) +# but only one level for Darwin + +libdiagnostics.install-common: installdirs libdiagnostics.install-headers + $(INSTALL_PROGRAM) $(LIBDIAGNOSTICS_FILENAME) \ + $(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_FILENAME) + ln -sf \ + $(LIBDIAGNOSTICS_SONAME_SYMLINK)\ + $(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK) + +else +libdiagnostics.install-common: installdirs libdiagnostics.install-headers + $(INSTALL_PROGRAM) $(LIBDIAGNOSTICS_FILENAME) \ + $(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_FILENAME) + ln -sf \ + $(LIBDIAGNOSTICS_FILENAME) \ + $(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_SONAME_SYMLINK) + ln -sf \ + $(LIBDIAGNOSTICS_SONAME_SYMLINK)\ + $(DESTDIR)$(libdir)/$(LIBDIAGNOSTICS_LINKER_NAME_SYMLINK) +endif +endif + +install-libdiagnostics: libdiagnostics.install-common + # Install the info files. # $(INSTALL_DATA) might be a relative pathname, so we can't cd into srcdir # to do the install. diff --git a/gcc/configure b/gcc/configure index 557ea5fa3ac9..5e2f4644c528 100755 --- a/gcc/configure +++ b/gcc/configure @@ -637,6 +637,8 @@ LD_PICFLAG PICFLAG enable_default_pie enable_host_bind_now +LIBDIAGNOSTICS +enable_libdiagnostics enable_host_pie enable_host_shared enable_plugin @@ -1051,6 +1053,7 @@ enable_version_specific_runtime_libs enable_plugin enable_host_shared enable_host_pie +enable_libdiagnostics enable_host_bind_now enable_libquadmath_support with_linker_hash_style @@ -1824,6 +1827,7 @@ Optional Features: --enable-plugin enable plugin support --enable-host-shared build host code as shared libraries --enable-host-pie build host code as PIE + --enable-libdiagnostics build libdiagnostics shared library --enable-host-bind-now link host code as BIND_NOW --disable-libquadmath-support disable libquadmath support for Fortran @@ -21406,7 +21410,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 21409 "configure" +#line 21413 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -21512,7 +21516,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 21515 "configure" +#line 21519 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -33788,6 +33792,9 @@ for language in $all_selected_languages do check_languages="$check_languages check-$language" done +if test x$enable_libdiagnostics = xyes; then + check_languages="$check_languages check-libdiagnostics" +fi selftest_languages= for language in $all_selected_languages @@ -34226,6 +34233,21 @@ fi +# Check whether --enable-libdiagnostics was given. +if test "${enable_libdiagnostics+set}" = set; then : + enableval=$enable_libdiagnostics; +fi + + + +if test "$enable_libdiagnostics" = "yes"; then + LIBDIAGNOSTICS='libdiagnostics' +else + LIBDIAGNOSTICS='' +fi + + + # Enable --enable-host-bind-now # Check whether --enable-host-bind-now was given. if test "${enable_host_bind_now+set}" = set; then : diff --git a/gcc/configure.ac b/gcc/configure.ac index eaa01d0d7e56..0edd1b92f5fb 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -7371,6 +7371,9 @@ for language in $all_selected_languages do check_languages="$check_languages check-$language" done +if test x$enable_libdiagnostics = xyes; then + check_languages="$check_languages check-libdiagnostics" +fi selftest_languages= for language in $all_selected_languages @@ -7603,6 +7606,19 @@ AC_ARG_ENABLE(host-pie, [build host code as PIE])]) AC_SUBST(enable_host_pie) +AC_ARG_ENABLE(libdiagnostics, +[AS_HELP_STRING([--enable-libdiagnostics], + [build libdiagnostics shared library])]) +AC_SUBST(enable_libdiagnostics) + +if test "$enable_libdiagnostics" = "yes"; then + LIBDIAGNOSTICS='libdiagnostics' +else + LIBDIAGNOSTICS='' +fi +AC_SUBST(LIBDIAGNOSTICS) + + # Enable --enable-host-bind-now AC_ARG_ENABLE(host-bind-now, [AS_HELP_STRING([--enable-host-bind-now], diff --git a/gcc/diagnostic-event-id.h b/gcc/diagnostic-event-id.h index 78c2ccbbc99d..51e802e149b7 100644 --- a/gcc/diagnostic-event-id.h +++ b/gcc/diagnostic-event-id.h @@ -47,6 +47,12 @@ class diagnostic_event_id_t return m_index + 1; } + int zero_based () const + { + gcc_assert (known_p ()); + return m_index; + } + private: static const int UNKNOWN_EVENT_IDX = -1; int m_index; // zero-based diff --git a/gcc/doc/install.texi b/gcc/doc/install.texi index 4973f195daf9..f1d64833cc8a 100644 --- a/gcc/doc/install.texi +++ b/gcc/doc/install.texi @@ -1226,6 +1226,13 @@ virtual calls in verifiable mode at all. However the libvtv library will still be built (see @option{--disable-libvtv} to turn off building libvtv). @option{--disable-vtable-verify} is the default. +@item --enable-libdiagnostics +Specify whether to build @code{libdiagnostics}, a shared library exposing +GCC's diagnostics capabilities via a C API, and a C++ wrapper API adding +``syntactic sugar''. + +This option requires @option{--enable-host-shared} on non-Windows hosts. + @item --disable-gcov Specify that the run-time library used for coverage analysis and associated host tools should not be built. diff --git a/gcc/libdiagnostics.cc b/gcc/libdiagnostics.cc new file mode 100644 index 000000000000..00afafe396b2 --- /dev/null +++ b/gcc/libdiagnostics.cc @@ -0,0 +1,1652 @@ +/* C++ implementation of a pure C API for emitting diagnostics. + Copyright (C) 2023-2024 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-path.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_physical_location +{ + diagnostic_physical_location (diagnostic_manager *mgr, + location_t inner) + : m_mgr (mgr), + m_inner (inner) + {} + + diagnostic_manager *m_mgr; + location_t m_inner; +}; + +static location_t +as_location_t (const diagnostic_physical_location *loc) +{ + if (!loc) + return UNKNOWN_LOCATION; + return loc->m_inner; +} + +/* 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; + } + } + + enum diagnostic_logical_location_kind_t get_external_kind () const + { + return m_kind; + } + + const diagnostic_logical_location *get_parent () const { return m_parent; } + + label_text get_name_for_path_output () const + { + return label_text::borrow (m_short_name.get_str ()); + } + +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; +}; + +static diagnostic_event_id +as_diagnostic_event_id (diagnostic_event_id_t id) +{ + return id.zero_based (); +} + +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); + + diagnostic_manager &m_mgr; + + /* One context per sink. */ + diagnostic_context m_dc; +}; + +/* This has to be a "struct" as it is exposed in the C API. */ + +struct diagnostic_text_sink : public sink +{ +public: + diagnostic_text_sink (diagnostic_manager &mgr, + FILE *dst_stream, + enum diagnostic_colorize colorize); + + void + on_begin_text_diagnostic (const diagnostic_info *info); + + diagnostic_source_printing_options &get_source_printing_options () + { + return m_dc.m_source_printing; + } + + void + set_colorize (enum diagnostic_colorize colorize); + +private: + const diagnostic_logical_location *m_current_logical_loc; +}; + +class sarif_sink : public sink +{ +public: + sarif_sink (diagnostic_manager &mgr, + FILE *dst_stream, + const diagnostic_file *main_input_file, + 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), + m_edit_context (m_file_cache) + { + 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; + + for (auto iter :m_location_t_map) + delete iter.second; + + free (m_line_table.m_location_adhoc_data_map.data); + free (m_line_table.info_ordinary.maps); + } + + 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; + } + + const diagnostic_physical_location * + new_location_from_file_and_line (const diagnostic_file *file, + diagnostic_line_num_t line_num) + { + ensure_linemap_for_file_and_line (file, line_num); + location_t loc = linemap_position_for_column (&m_line_table, 0); + return new_location (loc); + } + + const diagnostic_physical_location * + new_location_from_file_line_column (const diagnostic_file *file, + diagnostic_line_num_t line_num, + diagnostic_column_num_t column_num) + { + ensure_linemap_for_file_and_line (file, line_num); + location_t loc = linemap_position_for_column (&m_line_table, column_num); + return new_location (loc); + } + + const diagnostic_physical_location * + new_location_from_range (const diagnostic_physical_location *loc_caret, + const diagnostic_physical_location *loc_start, + const diagnostic_physical_location *loc_end) + { + return new_location + (m_line_table.make_location (as_location_t (loc_caret), + as_location_t (loc_start), + as_location_t (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; + } + + diagnostic_execution_path * + new_execution_path (); + + 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_physical_location (const diagnostic_physical_location *loc) const + { + if (!loc) + return; + gcc_assert (loc->m_mgr == this); + } + + /* TODO: Various things still use 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 () const + { + line_table = const_cast (&m_line_table); + } + +private: + void + ensure_linemap_for_file_and_line (const diagnostic_file *file, + diagnostic_line_num_t linenum) + { + /* 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 + { + line_map *map + = const_cast + (linemap_add (&m_line_table, LC_RENAME_VERBATIM, false, + file->get_name (), 0)); + ((line_map_ordinary *)map)->included_from = UNKNOWN_LOCATION; + } + linemap_line_start (&m_line_table, linenum, 100); + } + + const diagnostic_physical_location * + new_location (location_t loc) + { + if (loc == UNKNOWN_LOCATION) + return nullptr; + if (diagnostic_physical_location **slot = m_location_t_map.get (loc)) + return *slot; + diagnostic_physical_location *phys_loc + = new diagnostic_physical_location (this, loc); + m_location_t_map.put (loc, phys_loc); + return phys_loc; + } + + 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; + hash_map, + diagnostic_physical_location *> m_location_t_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; +}; + +class libdiagnostics_path_event : public diagnostic_event +{ +public: + libdiagnostics_path_event (const diagnostic_physical_location *physical_loc, + const diagnostic_logical_location *logical_loc, + unsigned stack_depth, + const char *gmsgid, + va_list *args) + : m_physical_loc (physical_loc), + m_logical_loc (logical_loc), + m_stack_depth (stack_depth) + { + m_desc_uncolored = make_desc (gmsgid, args, false); + m_desc_colored = make_desc (gmsgid, args, true); + } + + /* diagnostic_event vfunc implementations. */ + + location_t get_location () const final override + { + return as_location_t (m_physical_loc); + } + + int get_stack_depth () const final override + { + return m_stack_depth; + } + + label_text get_desc (bool can_colorize) const final override + { + const label_text &text = can_colorize ? m_desc_colored : m_desc_uncolored; + return label_text::borrow (text.get ()); + } + + const logical_location *get_logical_location () const + { + return m_logical_loc; + } + + meaning get_meaning () const final override + { + return meaning (); + } + + bool connect_to_next_event_p () const final override + { + return false; // TODO + } + + diagnostic_thread_id_t get_thread_id () const final override + { + return 0; + } + +private: + static label_text make_desc (const char *gmsgid, + va_list *args, + bool colorize) + { + va_list copy_of_args; + va_copy (copy_of_args, *args); + + // TODO: when should localization happen? + text_info text (gmsgid, ©_of_args, errno); + pretty_printer pp; + pp_show_color (&pp) = colorize; + pp.set_output_stream (nullptr); + pp_format (&pp, &text); + pp_output_formatted_text (&pp, nullptr); + label_text result = label_text::take (xstrdup (pp_formatted_text (&pp))); + + va_end (copy_of_args); + + return result; + } + + const diagnostic_physical_location *m_physical_loc; + const diagnostic_logical_location *m_logical_loc; + unsigned m_stack_depth; + label_text m_desc_uncolored; + label_text m_desc_colored; +}; + +class libdiagnostics_path_thread : public diagnostic_thread +{ +public: + libdiagnostics_path_thread (const char *name) : m_name (name) {} + label_text get_name (bool) const final override + { + return label_text::borrow (m_name); + } + +private: + const char *m_name; // has been i18n-ed and formatted +}; + +/* This has to be a "struct" as it is exposed in the C API. */ + +struct diagnostic_execution_path : public diagnostic_path +{ + diagnostic_execution_path () + : m_thread ("") + { + } + + diagnostic_event_id_t + add_event_va (const diagnostic_physical_location *physical_loc, + const diagnostic_logical_location *logical_loc, + unsigned stack_depth, + const char *gmsgid, + va_list *args) + { + m_events.push_back (::make_unique (physical_loc, + logical_loc, + stack_depth, + gmsgid, + args)); + return m_events.size () - 1; + } + + /* diagnostic_path vfunc implementations. */ + + unsigned num_events () const final override + { + return m_events.size (); + } + const diagnostic_event & get_event (int idx) const final override + { + return *m_events[idx]; + } + unsigned num_threads () const final override { return 1; } + const diagnostic_thread & + get_thread (diagnostic_thread_id_t) const final override + { + return m_thread; + } + + bool + same_function_p (int event_idx_a, + int event_idx_b) const final override + { + const logical_location *logical_loc_a + = m_events[event_idx_a]->get_logical_location (); + const logical_location *logical_loc_b + = m_events[event_idx_b]->get_logical_location (); + + // TODO: + /* Pointer equality, so we may want to uniqify logical loc ptrs. */ + return logical_loc_a == logical_loc_b; + } + +private: + libdiagnostics_path_thread m_thread; + std::vector> m_events; +}; + +/* 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), + m_path (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; } + + 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 (const diagnostic_physical_location *loc) + { + m_rich_loc.set_range (0, as_location_t (loc), SHOW_RANGE_WITH_CARET); + } + + void + add_location (const diagnostic_physical_location *loc) + { + m_rich_loc.add_range (as_location_t (loc), + SHOW_RANGE_WITHOUT_CARET); + } + + void + add_location_with_label (const diagnostic_physical_location *loc, + const char *text) + { + std::unique_ptr label + = ::make_unique (text); + m_rich_loc.add_range (as_location_t (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; + } + + diagnostic_execution_path * + add_execution_path () + { + m_path = ::make_unique (); + m_rich_loc.set_path (m_path.get ()); + return m_path.get (); + } + + void + take_execution_path (diagnostic_execution_path *path) + { + m_path = std::unique_ptr (path); + m_rich_loc.set_path (path); + } + +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; + std::unique_ptr m_path; +}; + +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; + case DIAGNOSTIC_LEVEL_SORRY: + return DK_SORRY; + } +} + +/* 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; +GCC_DIAGNOSTIC_PUSH_IGNORED(-Wsuggest-attribute=format) + diagnostic_set_info (&info, msgid, args, diag.get_rich_location (), + diagnostic_t_from_diagnostic_level (diag.get_level ())); +GCC_DIAGNOSTIC_POP + 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.set_client_data_hooks (new impl_diagnostic_client_data_hooks (mgr)); +} + +sink::~sink () +{ + diagnostic_finish (&m_dc); +} + +/* struct diagnostic_text_sink : public sink. */ + +static diagnostic_color_rule_t +get_color_rule (enum diagnostic_colorize colorize) +{ + switch (colorize) + { + default: + gcc_unreachable (); + case DIAGNOSTIC_COLORIZE_IF_TTY: + return DIAGNOSTICS_COLOR_AUTO; + break; + case DIAGNOSTIC_COLORIZE_NO: + return DIAGNOSTICS_COLOR_NO; + break; + case DIAGNOSTIC_COLORIZE_YES: + return DIAGNOSTICS_COLOR_YES; + break; + } +} + +diagnostic_text_sink::diagnostic_text_sink (diagnostic_manager &mgr, + FILE *dst_stream, + enum diagnostic_colorize colorize) +: sink (mgr), + m_current_logical_loc (nullptr) +{ + m_dc.set_show_cwe (true); + m_dc.set_show_rules (true); + + diagnostic_color_init (&m_dc, get_color_rule (colorize)); + diagnostic_urls_init (&m_dc); + m_dc.printer->set_output_stream (dst_stream); + diagnostic_starter (&m_dc) + = [] (diagnostic_context *context, + const diagnostic_info *info) + { + diagnostic_text_sink *sink + = static_cast (context->m_client_aux_data); + sink->on_begin_text_diagnostic (info); + }; + m_dc.set_show_cwe (true); + m_dc.set_show_rules (true); + m_dc.m_show_column = true; + m_dc.m_source_printing.enabled = true; + m_dc.m_source_printing.colorize_source_p = true; + + /* We don't currently expose a way for clients to manipulate the + following. */ + m_dc.m_source_printing.show_labels_p = true; + m_dc.m_source_printing.show_line_numbers_p = true; + m_dc.m_source_printing.min_margin_width = 6; + m_dc.set_path_format (DPF_INLINE_EVENTS); +} + +void +diagnostic_text_sink::set_colorize (enum diagnostic_colorize colorize) +{ + diagnostic_color_init (&m_dc, get_color_rule (colorize)); +} + +void +diagnostic_text_sink::on_begin_text_diagnostic (const 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) + { + pp_set_prefix (m_dc.printer, nullptr); + 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; + // TODO: handle other cases + } + } + } + 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, + const diagnostic_file *main_input_file, + enum diagnostic_sarif_version) +: sink (mgr) +{ + const char *main_input_filename = main_input_file->get_name (); + diagnostic_output_format_init_sarif_stream (m_dc, + mgr.get_line_table (), + main_input_filename, + true, + dst_stream); +} + +/* struct diagnostic_manager. */ + +void +diagnostic_manager::write_patch (FILE *dst_stream) +{ + pretty_printer pp; + pp.set_output_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; +} + +diagnostic_execution_path * +diagnostic_manager::new_execution_path () +{ + return new diagnostic_execution_path (); +} + +/* Error-checking at the API boundary. */ + +#define FAIL_IF_NULL(PTR_ARG) \ + do { \ + GCC_DIAGNOSTIC_PUSH_IGNORED(-Wnonnull-compare) \ + if (!(PTR_ARG)) { \ + fprintf (stderr, "%s: %s must be non-NULL\n", \ + __func__, #PTR_ARG); \ + abort (); \ + } \ + GCC_DIAGNOSTIC_POP \ + } while (0) + +/* 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) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (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) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (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) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (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) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (value); + + diag_mgr->get_client_version_info ()->m_version_url.set (value); +} + +/* Public entrypoint. */ + +diagnostic_text_sink * +diagnostic_manager_add_text_sink (diagnostic_manager *diag_mgr, + FILE *dst_stream, + enum diagnostic_colorize colorize) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (dst_stream); + + diagnostic_text_sink *result + = new diagnostic_text_sink (*diag_mgr, dst_stream, colorize); + diag_mgr->add_sink (std::unique_ptr (result)); + return result; +} + +/* Public entrypoint. */ + +void +diagnostic_text_sink_set_source_printing_enabled (diagnostic_text_sink *text_sink, + int value) +{ + FAIL_IF_NULL (text_sink); + + text_sink->get_source_printing_options ().enabled = value; +} + +/* Public entrypoint. */ + +void +diagnostic_text_sink_set_colorize (diagnostic_text_sink *text_sink, + enum diagnostic_colorize colorize) +{ + FAIL_IF_NULL (text_sink); + + text_sink->set_colorize (colorize); +} + +/* Public entrypoint. */ + +void +diagnostic_text_sink_set_labelled_source_colorization_enabled (diagnostic_text_sink *text_sink, + int value) +{ + FAIL_IF_NULL (text_sink); + + text_sink->get_source_printing_options ().colorize_source_p = value; +} + + +/* Public entrypoint. */ + +void +diagnostic_manager_add_sarif_sink (diagnostic_manager *diag_mgr, + FILE *dst_stream, + const diagnostic_file *main_input_file, + enum diagnostic_sarif_version version) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (dst_stream); + FAIL_IF_NULL (main_input_file); + + diag_mgr->add_sink (make_unique (*diag_mgr, + dst_stream, + main_input_file, + version)); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_write_patch (diagnostic_manager *diag_mgr, + FILE *dst_stream) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (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) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (name); + + return diag_mgr->new_file (name, sarif_source_language); +} + +void +diagnostic_manager_debug_dump_file (diagnostic_manager *, + const diagnostic_file *file, + FILE *out) +{ + FAIL_IF_NULL (out); + if (file) + { + if (file->get_sarif_source_language ()) + { + fprintf (out, "file(name=\"%s\", sarif_source_language=\"%s\")", + file->get_name (), + file->get_sarif_source_language ()); + } + else + { + fprintf (out, "file(name=\"%s\")", + file->get_name ()); + } + } + else + fprintf (out, "(null)"); +} + + +/* Public entrypoint. */ + +const diagnostic_physical_location * +diagnostic_manager_new_location_from_file_and_line (diagnostic_manager *diag_mgr, + const diagnostic_file *file, + diagnostic_line_num_t linenum) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (file); + + return diag_mgr->new_location_from_file_and_line (file, linenum); +} + +/* Public entrypoint. */ + +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) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (file); + + return diag_mgr->new_location_from_file_line_column (file, + line_num, + column_num); +} + +/* Public entrypoint. */ + +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) +{ + FAIL_IF_NULL (diag_mgr); + + return diag_mgr->new_location_from_range (loc_caret, + loc_start, + loc_end); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_debug_dump_location (const diagnostic_manager *diag_mgr, + const diagnostic_physical_location *loc, + FILE *out) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (out); + + if (loc) + { + const location_t cpplib_loc = as_location_t (loc); + diag_mgr->set_line_table_global (); + const expanded_location exp_loc (expand_location (cpplib_loc)); + + diagnostic_context dc; + diagnostic_initialize (&dc, 0); + dc.m_show_column = true; + + label_text loc_text = dc.get_location_text (exp_loc); + fprintf (out, "%s", loc_text.get ()); + + diagnostic_finish (&dc); + } + else + fprintf (out, "(null)"); +} + +/* 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) +{ + FAIL_IF_NULL (diag_mgr); + + return diag_mgr->new_logical_location (kind, + parent, + short_name, + fully_qualified_name, + decorated_name); +} + +void +diagnostic_manager_debug_dump_logical_location (const diagnostic_manager *diag_mgr, + const diagnostic_logical_location *loc, + FILE *out) +{ + FAIL_IF_NULL (diag_mgr); + FAIL_IF_NULL (out); + + if (loc) + { + fprintf (out, "logical_location(kind="); + switch (loc->get_external_kind ()) + { + default: + gcc_unreachable (); + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION: + fprintf (out, "function"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER: + fprintf (out, "member"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE: + fprintf (out, "module"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE: + fprintf (out, "namespace"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE: + fprintf (out, "file"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE: + fprintf (out, "return_type"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER: + fprintf (out, "parameter"); + break; + case DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE: + fprintf (out, "variable"); + break; + } + if (const diagnostic_logical_location *parent = loc->get_parent ()) + diagnostic_manager_debug_dump_logical_location (diag_mgr, + parent, + out); + if (const char *val = loc->get_short_name ()) + fprintf (out, ", short_name=\"%s\"", val); + if (const char *val = loc->get_name_with_scope ()) + fprintf (out, ", fully_qualified_name=\"%s\"", val); + if (const char *val = loc->get_internal_name ()) + fprintf (out, ", decorated_name=\"%s\"", val); + fprintf (out, ")"); + } + else + fprintf (out, "(null)"); +} + +/* Public entrypoint. */ + +void +diagnostic_manager_begin_group (diagnostic_manager *diag_mgr) +{ + FAIL_IF_NULL (diag_mgr); + diag_mgr->begin_group (); +} + +/* Public entrypoint. */ + +extern void +diagnostic_manager_end_group (diagnostic_manager *diag_mgr) +{ + FAIL_IF_NULL (diag_mgr); + diag_mgr->end_group (); +} + +/* Public entrypoint. */ + +diagnostic * +diagnostic_begin (diagnostic_manager *diag_mgr, + enum diagnostic_level level) +{ + FAIL_IF_NULL (diag_mgr); + + return new diagnostic (*diag_mgr, level); +} + +/* Public entrypoint. */ + +void +diagnostic_set_cwe (diagnostic *diag, + unsigned cwe_id) +{ + FAIL_IF_NULL (diag); + + diag->set_cwe (cwe_id); +} + +/* Public entrypoint. */ + +void +diagnostic_add_rule (diagnostic *diag, + const char *title, + const char *url) +{ + FAIL_IF_NULL (diag); + + diag->add_rule (title, url); +} + +/* Public entrypoint. */ + +void +diagnostic_set_location (diagnostic *diag, + const diagnostic_physical_location *loc) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + + diag->set_location (loc); +} + +/* Public entrypoint. */ + +void +diagnostic_add_location (diagnostic *diag, + const diagnostic_physical_location *loc) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + + diag->add_location (loc); +} + +/* Public entrypoint. */ + +void +diagnostic_add_location_with_label (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *text) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + FAIL_IF_NULL (text); + + diag->add_location_with_label (loc, text); +} + +/* Public entrypoint. */ + +void +diagnostic_set_logical_location (diagnostic *diag, + const diagnostic_logical_location *logical_loc) +{ + FAIL_IF_NULL (diag); + + diag->set_logical_location (logical_loc); +} + +/* Public entrypoint. */ + +void +diagnostic_add_fix_it_hint_insert_before (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *addition) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + FAIL_IF_NULL (addition); + + diag->get_manager ().set_line_table_global (); + diag->get_rich_location ()->add_fixit_insert_before (as_location_t (loc), + addition); +} + +/* Public entrypoint. */ + +void +diagnostic_add_fix_it_hint_insert_after (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *addition) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + FAIL_IF_NULL (addition); + + diag->get_manager ().set_line_table_global (); + diag->get_rich_location ()->add_fixit_insert_after (as_location_t (loc), + addition); +} + +/* Public entrypoint. */ + +void +diagnostic_add_fix_it_hint_replace (diagnostic *diag, + const diagnostic_physical_location *loc, + const char *replacement) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + FAIL_IF_NULL (replacement); + + diag->get_manager ().set_line_table_global (); + diag->get_rich_location ()->add_fixit_replace (as_location_t (loc), + replacement); +} + +/* Public entrypoint. */ + +void +diagnostic_add_fix_it_hint_delete (diagnostic *diag, + const diagnostic_physical_location *loc) +{ + FAIL_IF_NULL (diag); + diag->get_manager ().assert_valid_diagnostic_physical_location (loc); + + diag->get_manager ().set_line_table_global (); + diag->get_rich_location ()->add_fixit_remove (as_location_t (loc)); +} + +/* Public entrypoint. */ + +diagnostic_execution_path * +diagnostic_add_execution_path (diagnostic *diag) +{ + FAIL_IF_NULL (diag); + + return diag->add_execution_path (); +} + +/* Public entrypoint. */ + +diagnostic_execution_path * +diagnostic_manager_new_execution_path (diagnostic_manager *manager) +{ + FAIL_IF_NULL (manager); + + return manager->new_execution_path (); +} + +/* Public entrypoint. */ + +extern void +diagnostic_take_execution_path (diagnostic *diag, + diagnostic_execution_path *path) +{ + FAIL_IF_NULL (diag); + FAIL_IF_NULL (path); + + return diag->take_execution_path (path); +} + +/* Public entrypoint. */ + +void +diagnostic_execution_path_release (diagnostic_execution_path *path) +{ + delete path; +} + +/* Public entrypoint. */ + +diagnostic_event_id +diagnostic_execution_path_add_event (diagnostic_execution_path *path, + const diagnostic_physical_location *physical_loc, + const diagnostic_logical_location *logical_loc, + unsigned stack_depth, + const char *gmsgid, ...) +{ + FAIL_IF_NULL (path); + FAIL_IF_NULL (gmsgid); + + va_list args; + va_start (args, gmsgid); + diagnostic_event_id_t result = path->add_event_va (physical_loc, + logical_loc, + stack_depth, + gmsgid, &args); + va_end (args); + + return as_diagnostic_event_id (result); +} + +/* Public entrypoint. */ + +diagnostic_event_id +diagnostic_execution_path_add_event_va (diagnostic_execution_path *path, + const diagnostic_physical_location *physical_loc, + const diagnostic_logical_location *logical_loc, + unsigned stack_depth, + const char *gmsgid, + va_list *args) +{ + FAIL_IF_NULL (path); + FAIL_IF_NULL (gmsgid); + + diagnostic_event_id_t result = path->add_event_va (physical_loc, + logical_loc, + stack_depth, + gmsgid, args); + return as_diagnostic_event_id (result); +} + +/* Public entrypoint. */ + +void +diagnostic_finish (diagnostic *diag, const char *gmsgid, ...) +{ + FAIL_IF_NULL (diag); + + va_list args; + va_start (args, gmsgid); + diagnostic_finish_va (diag, gmsgid, &args); + va_end (args); +} + +/* Public entrypoint. */ + +void +diagnostic_finish_va (diagnostic *diag, const char *gmsgid, va_list *args) +{ + FAIL_IF_NULL (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; + diag->get_manager ().emit (*diag, gmsgid, args); + delete diag; +} diff --git a/gcc/libdiagnostics.map b/gcc/libdiagnostics.map new file mode 100644 index 000000000000..cc32b76d016a --- /dev/null +++ b/gcc/libdiagnostics.map @@ -0,0 +1,72 @@ +# Linker script for libdiagnostics.so +# Copyright (C) 2023-2024 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_text_sink_set_source_printing_enabled; + diagnostic_text_sink_set_colorize; + diagnostic_text_sink_set_labelled_source_colorization_enabled; + diagnostic_manager_add_sarif_sink; + diagnostic_manager_write_patch; + diagnostic_manager_new_file; + diagnostic_manager_debug_dump_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_debug_dump_location; + diagnostic_manager_new_logical_location; + diagnostic_manager_debug_dump_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_add_execution_path; + diagnostic_manager_new_execution_path; + diagnostic_take_execution_path; + diagnostic_execution_path_release; + diagnostic_execution_path_add_event; + diagnostic_execution_path_add_event_va; + + diagnostic_finish; + diagnostic_finish_va; + + local: *; +};