Commit 6d4a35ca by David Malcolm Committed by David Malcolm

Add diagnostic_metadata and CWE support

This patch adds support for associating a diagnostic message with an
optional diagnostic_metadata object, so that plugins can add extra data
to their diagnostics (e.g. mapping a diagnostic to a taxonomy or coding
standard such as from CERT or MISRA).

Currently this only supports associating a CWE identifier with a
diagnostic (which is what I'm using for the warnings in the analyzer
patch kit), but adding a diagnostic_metadata class allows for future
growth in this area without an explosion of further "warning_at"
overloads for all of the different kinds of custom data that a plugin
might want to add.

This version of the patch renames the overly-general
-fdiagnostics-show-metadata to -fdiagnostics-show-cwe and adds test
coverage for it via a plugin.

It also adds a note to the documentation that no GCC diagnostics
currently use this; it's a feature for plugins (and, at some point,
I hope, the analyzer).

gcc/ChangeLog:
	* common.opt (fdiagnostics-show-cwe): Add.
	* diagnostic-core.h (class diagnostic_metadata): New forward decl.
	(warning_at): Add overload taking a const diagnostic_metadata &.
	(emit_diagnostic_valist): Add overload taking a
	const diagnostic_metadata *.
	* diagnostic-format-json.cc: Include "diagnostic-metadata.h".
	(json_from_metadata): New function.
	(json_end_diagnostic): Call it to add "metadata" child for
	diagnostics with metadata.
	(diagnostic_output_format_init): Clear context->show_cwe.
	* diagnostic-metadata.h: New file.
	* diagnostic.c: Include "diagnostic-metadata.h".
	(diagnostic_impl): Add const diagnostic_metadata * param.
	(diagnostic_n_impl): Likewise.
	(diagnostic_initialize): Initialize context->show_cwe.
	(diagnostic_set_info_translated): Initialize diagnostic->metadata.
	(get_cwe_url): New function.
	(print_any_cwe): New function.
	(diagnostic_report_diagnostic): Call print_any_cwe if the
	diagnostic has non-NULL metadata.
	(emit_diagnostic): Pass NULL as the metadata in the call to
	diagnostic_impl.
	(emit_diagnostic_valist): Likewise.
	(emit_diagnostic_valist): New overload taking a
	const diagnostic_metadata *.
	(inform): Pass NULL as the metadata in the call to
	diagnostic_impl.
	(inform_n): Likewise for diagnostic_n_impl.
	(warning): Likewise.
	(warning_at): Likewise.  Add overload that takes a
	const diagnostic_metadata &.
	(warning_n): Pass NULL as the metadata in the call to
	diagnostic_n_impl.
	(pedwarn): Likewise for diagnostic_impl.
	(permerror): Likewise.
	(error): Likewise.
	(error_n): Likewise.
	(error_at): Likewise.
	(sorry): Likewise.
	(sorry_at): Likewise.
	(fatal_error): Likewise.
	(internal_error): Likewise.
	(internal_error_no_backtrace): Likewise.
	* diagnostic.h (diagnostic_info::metadata): New field.
	(diagnostic_context::show_cwe): New field.
	* doc/invoke.texi (-fno-diagnostics-show-cwe): New option.
	* opts.c (common_handle_option): Handle OPT_fdiagnostics_show_cwe.
	* toplev.c (general_init): Initialize global_dc->show_cwe.

gcc/testsuite/ChangeLog:
	* gcc.dg/plugin/diagnostic-test-metadata.c: New test.
	* gcc.dg/plugin/diagnostic_plugin_test_metadata.c: New test plugin.
	* gcc.dg/plugin/plugin.exp (plugin_test_list): Add them.

From-SVN: r279556
parent a7a09efa
2019-12-18 David Malcolm <dmalcolm@redhat.com>
* common.opt (fdiagnostics-show-cwe): Add.
* diagnostic-core.h (class diagnostic_metadata): New forward decl.
(warning_at): Add overload taking a const diagnostic_metadata &.
(emit_diagnostic_valist): Add overload taking a
const diagnostic_metadata *.
* diagnostic-format-json.cc: Include "diagnostic-metadata.h".
(json_from_metadata): New function.
(json_end_diagnostic): Call it to add "metadata" child for
diagnostics with metadata.
(diagnostic_output_format_init): Clear context->show_cwe.
* diagnostic-metadata.h: New file.
* diagnostic.c: Include "diagnostic-metadata.h".
(diagnostic_impl): Add const diagnostic_metadata * param.
(diagnostic_n_impl): Likewise.
(diagnostic_initialize): Initialize context->show_cwe.
(diagnostic_set_info_translated): Initialize diagnostic->metadata.
(get_cwe_url): New function.
(print_any_cwe): New function.
(diagnostic_report_diagnostic): Call print_any_cwe if the
diagnostic has non-NULL metadata.
(emit_diagnostic): Pass NULL as the metadata in the call to
diagnostic_impl.
(emit_diagnostic_valist): Likewise.
(emit_diagnostic_valist): New overload taking a
const diagnostic_metadata *.
(inform): Pass NULL as the metadata in the call to
diagnostic_impl.
(inform_n): Likewise for diagnostic_n_impl.
(warning): Likewise.
(warning_at): Likewise. Add overload that takes a
const diagnostic_metadata &.
(warning_n): Pass NULL as the metadata in the call to
diagnostic_n_impl.
(pedwarn): Likewise for diagnostic_impl.
(permerror): Likewise.
(error): Likewise.
(error_n): Likewise.
(error_at): Likewise.
(sorry): Likewise.
(sorry_at): Likewise.
(fatal_error): Likewise.
(internal_error): Likewise.
(internal_error_no_backtrace): Likewise.
* diagnostic.h (diagnostic_info::metadata): New field.
(diagnostic_context::show_cwe): New field.
* doc/invoke.texi (-fno-diagnostics-show-cwe): New option.
* opts.c (common_handle_option): Handle OPT_fdiagnostics_show_cwe.
* toplev.c (general_init): Initialize global_dc->show_cwe.
2019-12-19 Julian Brown <julian@codesourcery.com> 2019-12-19 Julian Brown <julian@codesourcery.com>
Maciej W. Rozycki <macro@codesourcery.com> Maciej W. Rozycki <macro@codesourcery.com>
Tobias Burnus <tobias@codesourcery.com> Tobias Burnus <tobias@codesourcery.com>
...@@ -1334,6 +1334,10 @@ fdiagnostics-show-option ...@@ -1334,6 +1334,10 @@ fdiagnostics-show-option
Common Var(flag_diagnostics_show_option) Init(1) Common Var(flag_diagnostics_show_option) Init(1)
Amend appropriate diagnostic messages with the command line option that controls them. Amend appropriate diagnostic messages with the command line option that controls them.
fdiagnostics-show-cwe
Common Var(flag_diagnostics_show_cwe) Init(1)
Print CWE identifiers for diagnostic messages, where available.
fdiagnostics-minimum-margin-width= fdiagnostics-minimum-margin-width=
Common Joined UInteger Var(diagnostics_minimum_margin_width) Init(6) Common Joined UInteger Var(diagnostics_minimum_margin_width) Init(6)
Set minimum width of left margin of source code when showing source. Set minimum width of left margin of source code when showing source.
......
...@@ -45,6 +45,9 @@ class auto_diagnostic_group ...@@ -45,6 +45,9 @@ class auto_diagnostic_group
~auto_diagnostic_group (); ~auto_diagnostic_group ();
}; };
/* Forward decl. */
class diagnostic_metadata; /* See diagnostic-metadata.h. */
extern const char *progname; extern const char *progname;
extern const char *trim_filename (const char *); extern const char *trim_filename (const char *);
...@@ -78,6 +81,9 @@ extern bool warning_at (location_t, int, const char *, ...) ...@@ -78,6 +81,9 @@ extern bool warning_at (location_t, int, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,4); ATTRIBUTE_GCC_DIAG(3,4);
extern bool warning_at (rich_location *, int, const char *, ...) extern bool warning_at (rich_location *, int, const char *, ...)
ATTRIBUTE_GCC_DIAG(3,4); ATTRIBUTE_GCC_DIAG(3,4);
extern bool warning_at (rich_location *, const diagnostic_metadata &, int,
const char *, ...)
ATTRIBUTE_GCC_DIAG(4,5);
extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2); extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *, extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *,
const char *, ...) const char *, ...)
...@@ -109,6 +115,10 @@ extern bool emit_diagnostic (diagnostic_t, rich_location *, int, ...@@ -109,6 +115,10 @@ extern bool emit_diagnostic (diagnostic_t, rich_location *, int,
const char *, ...) ATTRIBUTE_GCC_DIAG(4,5); const char *, ...) ATTRIBUTE_GCC_DIAG(4,5);
extern bool emit_diagnostic_valist (diagnostic_t, location_t, int, const char *, extern bool emit_diagnostic_valist (diagnostic_t, location_t, int, const char *,
va_list *) ATTRIBUTE_GCC_DIAG (4,0); va_list *) ATTRIBUTE_GCC_DIAG (4,0);
extern bool emit_diagnostic_valist (diagnostic_t, rich_location *,
const diagnostic_metadata *metadata,
int, const char *, va_list *)
ATTRIBUTE_GCC_DIAG (5,0);
extern bool seen_error (void); extern bool seen_error (void);
#ifdef BUFSIZ #ifdef BUFSIZ
......
...@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
#include "system.h" #include "system.h"
#include "coretypes.h" #include "coretypes.h"
#include "diagnostic.h" #include "diagnostic.h"
#include "diagnostic-metadata.h"
#include "json.h" #include "json.h"
#include "selftest.h" #include "selftest.h"
...@@ -103,6 +104,20 @@ json_from_fixit_hint (const fixit_hint *hint) ...@@ -103,6 +104,20 @@ json_from_fixit_hint (const fixit_hint *hint)
return fixit_obj; return fixit_obj;
} }
/* Generate a JSON object for METADATA. */
static json::object *
json_from_metadata (const diagnostic_metadata *metadata)
{
json::object *metadata_obj = new json::object ();
if (metadata->get_cwe ())
metadata_obj->set ("cwe",
new json::integer_number (metadata->get_cwe ()));
return metadata_obj;
}
/* No-op implementation of "begin_diagnostic" for JSON output. */ /* No-op implementation of "begin_diagnostic" for JSON output. */
static void static void
...@@ -211,6 +226,12 @@ json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic, ...@@ -211,6 +226,12 @@ json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
TODO: functions TODO: functions
TODO: inlining information TODO: inlining information
TODO: macro expansion information. */ TODO: macro expansion information. */
if (diagnostic->metadata)
{
json::object *metadata_obj = json_from_metadata (diagnostic->metadata);
diag_obj->set ("metadata", metadata_obj);
}
} }
/* No-op implementation of "begin_group_cb" for JSON output. */ /* No-op implementation of "begin_group_cb" for JSON output. */
...@@ -268,6 +289,9 @@ diagnostic_output_format_init (diagnostic_context *context, ...@@ -268,6 +289,9 @@ diagnostic_output_format_init (diagnostic_context *context,
context->end_group_cb = json_end_group; context->end_group_cb = json_end_group;
context->final_cb = json_final_cb; context->final_cb = json_final_cb;
/* The metadata is handled in JSON format, rather than as text. */
context->show_cwe = false;
/* The option is handled in JSON format, rather than as text. */ /* The option is handled in JSON format, rather than as text. */
context->show_option_requested = false; context->show_option_requested = false;
......
/* Additional metadata for a diagnostic.
Copyright (C) 2019 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>
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
<http://www.gnu.org/licenses/>. */
#ifndef GCC_DIAGNOSTIC_METADATA_H
#define GCC_DIAGNOSTIC_METADATA_H
/* A bundle of additional metadata that can be associated with a
diagnostic.
Currently this only supports associating a CWE identifier with a
diagnostic. */
class diagnostic_metadata
{
public:
diagnostic_metadata () : m_cwe (0) {}
void add_cwe (int cwe) { m_cwe = cwe; }
int get_cwe () const { return m_cwe; }
private:
int m_cwe;
};
#endif /* ! GCC_DIAGNOSTIC_METADATA_H */
...@@ -46,6 +46,10 @@ struct diagnostic_info ...@@ -46,6 +46,10 @@ struct diagnostic_info
/* The location at which the diagnostic is to be reported. */ /* The location at which the diagnostic is to be reported. */
rich_location *richloc; rich_location *richloc;
/* An optional bundle of metadata associated with the diagnostic
(or NULL). */
const diagnostic_metadata *metadata;
/* Auxiliary data for client. */ /* Auxiliary data for client. */
void *x_data; void *x_data;
/* The kind of diagnostic it is about. */ /* The kind of diagnostic it is about. */
...@@ -126,6 +130,10 @@ struct diagnostic_context ...@@ -126,6 +130,10 @@ struct diagnostic_context
/* Character used for caret diagnostics. */ /* Character used for caret diagnostics. */
char caret_chars[rich_location::STATICALLY_ALLOCATED_RANGES]; char caret_chars[rich_location::STATICALLY_ALLOCATED_RANGES];
/* True if we should print any CWE identifiers associated with
diagnostics. */
bool show_cwe;
/* True if we should print the command line option which controls /* True if we should print the command line option which controls
each diagnostic, if known. */ each diagnostic, if known. */
bool show_option_requested; bool show_option_requested;
......
...@@ -277,6 +277,7 @@ Objective-C and Objective-C++ Dialects}. ...@@ -277,6 +277,7 @@ Objective-C and Objective-C++ Dialects}.
-fdiagnostics-format=@r{[}text@r{|}json@r{]} @gol -fdiagnostics-format=@r{[}text@r{|}json@r{]} @gol
-fno-diagnostics-show-option -fno-diagnostics-show-caret @gol -fno-diagnostics-show-option -fno-diagnostics-show-caret @gol
-fno-diagnostics-show-labels -fno-diagnostics-show-line-numbers @gol -fno-diagnostics-show-labels -fno-diagnostics-show-line-numbers @gol
-fno-diagnostics-show-cwe @gol
-fdiagnostics-minimum-margin-width=@var{width} @gol -fdiagnostics-minimum-margin-width=@var{width} @gol
-fdiagnostics-parseable-fixits -fdiagnostics-generate-patch @gol -fdiagnostics-parseable-fixits -fdiagnostics-generate-patch @gol
-fdiagnostics-show-template-tree -fno-elide-type @gol -fdiagnostics-show-template-tree -fno-elide-type @gol
...@@ -4005,6 +4006,15 @@ as the types of expressions: ...@@ -4005,6 +4006,15 @@ as the types of expressions:
This option suppresses the printing of these labels (in the example above, This option suppresses the printing of these labels (in the example above,
the vertical bars and the ``char *'' and ``long int'' text). the vertical bars and the ``char *'' and ``long int'' text).
@item -fno-diagnostics-show-cwe
@opindex fno-diagnostics-show-cwe
@opindex fdiagnostics-show-cwe
Diagnostic messages can optionally have an associated
@url{https://cwe.mitre.org/index.html, CWE} identifier.
GCC itself does not do this for any of its diagnostics, but plugins may do so.
By default, if this information is present, it will be printed with
the diagnostic. This option suppresses the printing of this metadata.
@item -fno-diagnostics-show-line-numbers @item -fno-diagnostics-show-line-numbers
@opindex fno-diagnostics-show-line-numbers @opindex fno-diagnostics-show-line-numbers
@opindex fdiagnostics-show-line-numbers @opindex fdiagnostics-show-line-numbers
...@@ -2407,6 +2407,10 @@ common_handle_option (struct gcc_options *opts, ...@@ -2407,6 +2407,10 @@ common_handle_option (struct gcc_options *opts,
dc->parseable_fixits_p = value; dc->parseable_fixits_p = value;
break; break;
case OPT_fdiagnostics_show_cwe:
dc->show_cwe = value;
break;
case OPT_fdiagnostics_show_option: case OPT_fdiagnostics_show_option:
dc->show_option_requested = value; dc->show_option_requested = value;
break; break;
......
2019-12-18 David Malcolm <dmalcolm@redhat.com>
* gcc.dg/plugin/diagnostic-test-metadata.c: New test.
* gcc.dg/plugin/diagnostic_plugin_test_metadata.c: New test plugin.
* gcc.dg/plugin/plugin.exp (plugin_test_list): Add them.
2019-12-19 Jakub Jelinek <jakub@redhat.com> 2019-12-19 Jakub Jelinek <jakub@redhat.com>
PR fortran/92977 PR fortran/92977
......
/* { dg-do compile } */
extern char *gets (char *s);
void test_cwe (void)
{
char buf[1024];
gets (buf); /* { dg-warning "never use 'gets' \\\[CWE-242\\\]" } */
}
/* This plugin exercises diagnostic_metadata. */
#include "gcc-plugin.h"
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "stringpool.h"
#include "toplev.h"
#include "basic-block.h"
#include "hash-table.h"
#include "vec.h"
#include "ggc.h"
#include "basic-block.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "gimple-fold.h"
#include "tree-eh.h"
#include "gimple-expr.h"
#include "is-a.h"
#include "gimple.h"
#include "gimple-iterator.h"
#include "tree.h"
#include "tree-pass.h"
#include "intl.h"
#include "plugin-version.h"
#include "diagnostic.h"
#include "context.h"
#include "gcc-rich-location.h"
#include "diagnostic-metadata.h"
int plugin_is_GPL_compatible;
const pass_data pass_data_test_metadata =
{
GIMPLE_PASS, /* type */
"test_metadata", /* name */
OPTGROUP_NONE, /* optinfo_flags */
TV_NONE, /* tv_id */
PROP_ssa, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
0, /* todo_flags_finish */
};
class pass_test_metadata : public gimple_opt_pass
{
public:
pass_test_metadata(gcc::context *ctxt)
: gimple_opt_pass(pass_data_test_metadata, ctxt)
{}
/* opt_pass methods: */
bool gate (function *) { return true; }
virtual unsigned int execute (function *);
}; // class pass_test_metadata
/* Determine if STMT is a call with NUM_ARGS arguments to a function
named FUNCNAME.
If so, return STMT as a gcall *. Otherwise return NULL. */
static gcall *
check_for_named_call (gimple *stmt,
const char *funcname, unsigned int num_args)
{
gcc_assert (funcname);
gcall *call = dyn_cast <gcall *> (stmt);
if (!call)
return NULL;
tree fndecl = gimple_call_fndecl (call);
if (!fndecl)
return NULL;
if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname))
return NULL;
if (gimple_call_num_args (call) != num_args)
{
error_at (stmt->location, "expected number of args: %i (got %i)",
num_args, gimple_call_num_args (call));
return NULL;
}
return call;
}
/* Exercise diagnostic_metadata. */
unsigned int
pass_test_metadata::execute (function *fun)
{
gimple_stmt_iterator gsi;
basic_block bb;
FOR_EACH_BB_FN (bb, fun)
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
gimple *stmt = gsi_stmt (gsi);
/* Example of CWE: complain about uses of gets. */
if (gcall *call = check_for_named_call (stmt, "gets", 1))
{
gcc_rich_location richloc (gimple_location (call));
/* CWE-242: Use of Inherently Dangerous Function. */
diagnostic_metadata m;
m.add_cwe (242);
warning_at (&richloc, m, 0,
"never use %qs", "gets");
}
}
return 0;
}
int
plugin_init (struct plugin_name_args *plugin_info,
struct plugin_gcc_version *version)
{
struct register_pass_info pass_info;
const char *plugin_name = plugin_info->base_name;
int argc = plugin_info->argc;
struct plugin_argument *argv = plugin_info->argv;
if (!plugin_default_version_check (version, &gcc_version))
return 1;
pass_info.pass = new pass_test_metadata (g);
pass_info.reference_pass_name = "ssa";
pass_info.ref_pass_instance_number = 1;
pass_info.pos_op = PASS_POS_INSERT_AFTER;
register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
&pass_info);
return 0;
}
...@@ -94,6 +94,7 @@ set plugin_test_list [list \ ...@@ -94,6 +94,7 @@ set plugin_test_list [list \
diagnostic-test-inlining-2.c \ diagnostic-test-inlining-2.c \
diagnostic-test-inlining-3.c \ diagnostic-test-inlining-3.c \
diagnostic-test-inlining-4.c } \ diagnostic-test-inlining-4.c } \
{ diagnostic_plugin_test_metadata.c diagnostic-test-metadata.c } \
{ location_overflow_plugin.c \ { location_overflow_plugin.c \
location-overflow-test-1.c \ location-overflow-test-1.c \
location-overflow-test-2.c \ location-overflow-test-2.c \
......
...@@ -1179,6 +1179,8 @@ general_init (const char *argv0, bool init_signals) ...@@ -1179,6 +1179,8 @@ general_init (const char *argv0, bool init_signals)
= global_options_init.x_flag_diagnostics_show_labels; = global_options_init.x_flag_diagnostics_show_labels;
global_dc->show_line_numbers_p global_dc->show_line_numbers_p
= global_options_init.x_flag_diagnostics_show_line_numbers; = global_options_init.x_flag_diagnostics_show_line_numbers;
global_dc->show_cwe
= global_options_init.x_flag_diagnostics_show_cwe;
global_dc->show_option_requested global_dc->show_option_requested
= global_options_init.x_flag_diagnostics_show_option; = global_options_init.x_flag_diagnostics_show_option;
global_dc->min_margin_width global_dc->min_margin_width
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment