From patchwork Fri Dec 13 18:11:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 1209355 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-515941-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="a4kt8ULH"; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.b="OpYm2cbG"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 47ZJmt6zkVz9sR7 for ; Sat, 14 Dec 2019 05:17:50 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; q=dns; s= default; b=NWbT6M4Dkz8HO6SchyWTofc1aqJWrgV670yLKTFKV/TcxPxUn0ney 18+FUAzOX1QnHU+nIE7mnIvnTS5w2crS9tHma5ACmx5HliXuvidaF1uBf3/QGeLI P/F5o/yTvx0cBLBk7idVi4FiRRQ0/9BSaW7TpayUxU6C+T0rzzEH8M= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-type:content-transfer-encoding; s=default; bh=eadT3rR+/bLKmKFGi2J+Vxf3ySs=; b=a4kt8ULH4y5kiaUGA1684lr+46wb cyCL1tnvj/UTjf/7NJ7zLKgyecv8gWoN6R1Aw+zf51+xxR2YvLvwCgZHrNVHZZaj 60ni5RyAGtbmPxNIikv3ZjKfcoZl3fAZD73Rkx4HcMw2MVxmcDANS2pqvd3Rm5l1 WYTKixCcstag6Ms= Received: (qmail 103737 invoked by alias); 13 Dec 2019 18:12:26 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 102082 invoked by uid 89); 13 Dec 2019 18:12:08 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-22.8 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT autolearn=ham version=3.3.1 spammy= X-HELO: us-smtp-delivery-1.mimecast.com Received: from us-smtp-1.mimecast.com (HELO us-smtp-delivery-1.mimecast.com) (205.139.110.61) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 13 Dec 2019 18:11:59 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1576260717; 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=0lEEcg7DLpGglSs8JvquXHTam+3wjwtBXF651qf27p8=; b=OpYm2cbGKJeje5hBZT93rrVd5hEbEDUd+4+3rDYXZDBTzVnW2qiw6nXHDtdFxRlq+IAlgc C7/ClEuThEKherr5riYALW6clOMj7Uv0UuCa4AiI/wCnlXAEPt6IWub7w1di1MYET+wDkz ZbwjS4GNGFTq9nVrSolba/ogIVM8WtY= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-100-eobSAeS-MT6omqjb-ARZRA-1; Fri, 13 Dec 2019 13:11:54 -0500 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 873BC8017DF for ; Fri, 13 Dec 2019 18:11:53 +0000 (UTC) Received: from t470.redhat.com (ovpn-117-164.phx2.redhat.com [10.3.117.164]) by smtp.corp.redhat.com (Postfix) with ESMTP id 23FB25C241; Fri, 13 Dec 2019 18:11:53 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [PATCH 27/45] analyzer: new files: pending-diagnostic.{cc|h} Date: Fri, 13 Dec 2019 13:11:16 -0500 Message-Id: <20191213181134.1830-28-dmalcolm@redhat.com> In-Reply-To: <20191213181134.1830-1-dmalcolm@redhat.com> References: <20191213181134.1830-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-IsSubscribed: yes Changed in v4: - Remove include of gcc-plugin.h, reworking includes accordingly. - Wrap everything in #if ENABLE_ANALYZER - Capture const state_change_event& in evdesc_state_change: https://gcc.gnu.org/ml/gcc-patches/2019-12/msg00218.html This patch adds classes used by the analyzer for handling its diagnostics (queueing them, deduplicating them, precision-of-wording hooks). gcc/ChangeLog: * analyzer/pending-diagnostic.cc: New file. * analyzer/pending-diagnostic.h: New file. --- gcc/analyzer/pending-diagnostic.cc | 64 +++++++ gcc/analyzer/pending-diagnostic.h | 269 +++++++++++++++++++++++++++++ 2 files changed, 333 insertions(+) create mode 100644 gcc/analyzer/pending-diagnostic.cc create mode 100644 gcc/analyzer/pending-diagnostic.h diff --git a/gcc/analyzer/pending-diagnostic.cc b/gcc/analyzer/pending-diagnostic.cc new file mode 100644 index 000000000000..ae61b47bef66 --- /dev/null +++ b/gcc/analyzer/pending-diagnostic.cc @@ -0,0 +1,64 @@ +/* Classes for analyzer diagnostics. + Copyright (C) 2019 Free Software Foundation, Inc. + Contributed by David Malcolm . + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +GCC is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "intl.h" +#include "diagnostic.h" +#include "analyzer/analyzer.h" +#include "analyzer/pending-diagnostic.h" + +#if ENABLE_ANALYZER + +/* Generate a label_text by printing FMT. + + Use a clone of the global_dc for formatting callbacks. + + Use this evdesc::event_desc's m_colorize flag to control colorization + (so that e.g. we can disable it for JSON output). */ + +label_text +evdesc::event_desc::formatted_print (const char *fmt, ...) const +{ + pretty_printer *pp = global_dc->printer->clone (); + + pp_show_color (pp) = m_colorize; + + text_info ti; + rich_location rich_loc (line_table, UNKNOWN_LOCATION); + va_list ap; + va_start (ap, fmt); + ti.format_spec = _(fmt); + ti.args_ptr = ≈ + ti.err_no = 0; + ti.x_data = NULL; + ti.m_richloc = &rich_loc; + pp_format (pp, &ti); + pp_output_formatted_text (pp); + va_end (ap); + + label_text result = label_text::take (xstrdup (pp_formatted_text (pp))); + delete pp; + return result; +} + +#endif /* #if ENABLE_ANALYZER */ diff --git a/gcc/analyzer/pending-diagnostic.h b/gcc/analyzer/pending-diagnostic.h new file mode 100644 index 000000000000..15a1379e8fd1 --- /dev/null +++ b/gcc/analyzer/pending-diagnostic.h @@ -0,0 +1,269 @@ +/* Classes for analyzer diagnostics. + Copyright (C) 2019 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 +. */ + +#ifndef GCC_ANALYZER_PENDING_DIAGNOSTIC_H +#define GCC_ANALYZER_PENDING_DIAGNOSTIC_H + +#include "diagnostic-event-id.h" +#include "analyzer/sm.h" + +/* Various bundles of information used for generating more precise + messages for events within a diagnostic_path, for passing to the + various "describe_*" vfuncs of pending_diagnostic. See those + for more information. */ + +namespace evdesc { + +struct event_desc +{ + event_desc (bool colorize) : m_colorize (colorize) {} + + label_text formatted_print (const char *fmt, ...) const + ATTRIBUTE_GCC_DIAG(2,3); + + bool m_colorize; +}; + +/* For use by pending_diagnostic::describe_state_change. */ + +struct state_change : public event_desc +{ + state_change (bool colorize, + tree expr, + tree origin, + state_machine::state_t old_state, + state_machine::state_t new_state, + diagnostic_event_id_t event_id, + const state_change_event &event) + : event_desc (colorize), + m_expr (expr), m_origin (origin), + m_old_state (old_state), m_new_state (new_state), + m_event_id (event_id), m_event (event) + {} + + bool is_global_p () const { return m_expr == NULL_TREE; } + + tree m_expr; + tree m_origin; + state_machine::state_t m_old_state; + state_machine::state_t m_new_state; + diagnostic_event_id_t m_event_id; + const state_change_event &m_event; +}; + +/* For use by pending_diagnostic::describe_call_with_state. */ + +struct call_with_state : public event_desc +{ + call_with_state (bool colorize, + tree caller_fndecl, tree callee_fndecl, + tree expr, state_machine::state_t state) + : event_desc (colorize), + m_caller_fndecl (caller_fndecl), + m_callee_fndecl (callee_fndecl), + m_expr (expr), + m_state (state) + { + } + + tree m_caller_fndecl; + tree m_callee_fndecl; + tree m_expr; + state_machine::state_t m_state; +}; + +/* For use by pending_diagnostic::describe_return_of_state. */ + +struct return_of_state : public event_desc +{ + return_of_state (bool colorize, + tree caller_fndecl, tree callee_fndecl, + state_machine::state_t state) + : event_desc (colorize), + m_caller_fndecl (caller_fndecl), + m_callee_fndecl (callee_fndecl), + m_state (state) + { + } + + tree m_caller_fndecl; + tree m_callee_fndecl; + state_machine::state_t m_state; +}; + +/* For use by pending_diagnostic::describe_final_event. */ + +struct final_event : public event_desc +{ + final_event (bool colorize, + tree expr, state_machine::state_t state) + : event_desc (colorize), + m_expr (expr), m_state (state) + {} + + tree m_expr; + state_machine::state_t m_state; +}; + +} /* end of namespace evdesc */ + +/* An abstract base class for capturing information about a diagnostic in + a form that is ready to emit at a later point (or be rejected). + Each kind of diagnostic will have a concrete subclass of + pending_diagnostic. + + Normally, gcc diagnostics are emitted using va_list, which can't be + portably stored for later use, so we have to use an "emit" virtual + function. + + This class also supports comparison, so that multiple pending_diagnostic + instances can be de-duplicated. + + As well as emitting a diagnostic, the class has various "precision of + wording" virtual functions, for generating descriptions for events + within a diagnostic_path. These are optional, but implementing these + allows for more precise wordings than the more generic + implementation. */ + +class pending_diagnostic +{ + public: + virtual ~pending_diagnostic () {} + + /* Vfunc for emitting the diagnostic. The rich_location will have been + populated with a diagnostic_path. + Return true if a diagnostic is actually emitted. */ + virtual bool emit (rich_location *) = 0; + + /* Hand-coded RTTI: get an ID for the subclass. */ + virtual const char *get_kind () const = 0; + + /* Compare for equality with OTHER, which might be of a different + subclass. */ + + bool equal_p (const pending_diagnostic &other) + { + /* Check for pointer equality on the IDs from get_kind. */ + if (get_kind () != other.get_kind ()) + return false; + /* Call vfunc now we know they have the same ID: */ + return subclass_equal_p (other); + } + + /* A vfunc for testing for equality, where we've already + checked they have the same ID. See pending_diagnostic_subclass + below for a convenience subclass for implementing this. */ + virtual bool subclass_equal_p (const pending_diagnostic &other) const = 0; + + /* For greatest precision-of-wording, the various following "describe_*" + virtual functions give the pending diagnostic a way to describe events + in a diagnostic_path in terms that make sense for that diagnostic. + + In each case, return a non-NULL label_text to give the event a custom + description; NULL otherwise (falling back on a more generic + description). */ + + /* Precision-of-wording vfunc for describing a critical state change + within the diagnostic_path. + + For example, a double-free diagnostic might use the descriptions: + - "first 'free' happens here" + - "second 'free' happens here" + for the pertinent events, whereas a use-after-free might use the + descriptions: + - "freed here" + - "use after free here" + Note how in both cases the first event is a "free": the best + description to use depends on the diagnostic. */ + + virtual label_text describe_state_change (const evdesc::state_change &) + { + /* Default no-op implementation. */ + return label_text (); + } + + /* Precision-of-wording vfunc for describing an interprocedural call + carrying critial state for the diagnostic, from caller to callee. + + For example a double-free diagnostic might use: + - "passing freed pointer 'ptr' in call to 'deallocator' from 'test'" + to make it clearer how the freed value moves from caller to + callee. */ + + virtual label_text describe_call_with_state (const evdesc::call_with_state &) + { + /* Default no-op implementation. */ + return label_text (); + } + + /* Precision-of-wording vfunc for describing an interprocedural return + within the diagnostic_path that carries critial state for the + diagnostic, from callee back to caller. + + For example, a deref-of-unchecked-malloc diagnostic might use: + - "returning possibly-NULL pointer to 'make_obj' from 'allocator'" + to make it clearer how the unchecked value moves from callee + back to caller. */ + + virtual label_text describe_return_of_state (const evdesc::return_of_state &) + { + /* Default no-op implementation. */ + return label_text (); + } + + /* Precision-of-wording vfunc for describing the final event within a + diagnostic_path. + + For example a double-free diagnostic might use: + - "second 'free' here; first 'free' was at (3)" + and a use-after-free might use + - "use after 'free' here; memory was freed at (2)". */ + + virtual label_text describe_final_event (const evdesc::final_event &) + { + /* Default no-op implementation. */ + return label_text (); + } + + /* End of precision-of-wording vfuncs. */ +}; + +/* A template to make it easier to make subclasses of pending_diagnostic. + + This uses the curiously-recurring template pattern, to implement + pending_diagnostic::subclass_equal_p by casting and calling + the operator== + + This assumes that BASE_OTHER has already been checked to have + been of the same subclass (which pending_diagnostic::equal_p does). */ + +template +class pending_diagnostic_subclass : public pending_diagnostic +{ + public: + bool subclass_equal_p (const pending_diagnostic &base_other) const + FINAL OVERRIDE + { + const Subclass &other = (const Subclass &)base_other; + return *(const Subclass*)this == other; + } +}; + +#endif /* GCC_ANALYZER_PENDING_DIAGNOSTIC_H */