diagnostics: add ability to associate diagnostics with rules from coding standards

gcc/ChangeLog:
	* common.opt (fdiagnostics-show-rules): New option.
	* diagnostic-format-json.cc (diagnostic_output_format_init_json):
	Fix up context->show_rules.
	* diagnostic-format-sarif.cc
	(diagnostic_output_format_init_sarif): Likewise.
	* diagnostic-metadata.h (diagnostic_metadata::rule): New class.
	(diagnostic_metadata::precanned_rule): New class.
	(diagnostic_metadata::add_rule): New.
	(diagnostic_metadata::get_num_rules): New.
	(diagnostic_metadata::get_rule): New.
	(diagnostic_metadata::m_rules): New field.
	* diagnostic.cc (diagnostic_initialize): Initialize show_rules.
	(print_any_rules): New.
	(diagnostic_report_diagnostic): Call it.
	* diagnostic.h (diagnostic_context::show_rules): New field.
	* doc/invoke.texi (-fno-diagnostics-show-rules): New option.
	* opts.cc (common_handle_option): Handle
	OPT_fdiagnostics_show_rules.
	* toplev.cc (general_init): Set up global_dc->show_rules.

gcc/testsuite/ChangeLog:
	* gcc.dg/plugin/diagnostic-test-metadata.c: Expect " [STR34-C]" to
	be emitted at the "gets" call.
	* gcc.dg/plugin/diagnostic_plugin_test_metadata.c
	(pass_test_metadata::execute): Associate the "gets" diagnostic
	with a rule named "STR34-C".

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
David Malcolm 2022-06-23 14:59:24 -04:00
parent 7c1c7e120c
commit 0b14f590e3
11 changed files with 127 additions and 4 deletions

View File

@ -1466,6 +1466,10 @@ fdiagnostics-show-cwe
Common Var(flag_diagnostics_show_cwe) Init(1)
Print CWE identifiers for diagnostic messages, where available.
fdiagnostics-show-rules
Common Var(flag_diagnostics_show_rules) Init(1)
Print any rules associated with diagnostic messages.
fdiagnostics-path-format=
Common Joined RejectNegative Var(flag_diagnostics_path_format) Enum(diagnostic_path_format) Init(DPF_INLINE_EVENTS)
Specify how to print any control-flow path associated with a diagnostic.

View File

@ -345,6 +345,7 @@ diagnostic_output_format_init_json (diagnostic_context *context)
/* The metadata is handled in JSON format, rather than as text. */
context->show_cwe = false;
context->show_rules = false;
/* The option is handled in JSON format, rather than as text. */
context->show_option_requested = false;

View File

@ -1556,6 +1556,7 @@ diagnostic_output_format_init_sarif (diagnostic_context *context)
/* The metadata is handled in SARIF format, rather than as text. */
context->show_cwe = false;
context->show_rules = false;
/* The option is handled in SARIF format, rather than as text. */
context->show_option_requested = false;

View File

@ -24,19 +24,62 @@ along with GCC; see the file COPYING3. If not see
/* A bundle of additional metadata that can be associated with a
diagnostic.
Currently this only supports associating a CWE identifier with a
diagnostic. */
This supports an optional CWE identifier, and zero or more
"rules". */
class diagnostic_metadata
{
public:
/* Abstract base class for referencing a rule that has been violated,
such as within a coding standard, or within a specification. */
class rule
{
public:
virtual char *make_description () const = 0;
virtual char *make_url () const = 0;
};
/* Concrete subclass. */
class precanned_rule : public rule
{
public:
precanned_rule (const char *desc, const char *url)
: m_desc (desc), m_url (url)
{}
char *make_description () const final override
{
return m_desc ? xstrdup (m_desc) : NULL;
}
char *make_url () const final override
{
return m_url ? xstrdup (m_url) : NULL;
}
private:
const char *m_desc;
const char *m_url;
};
diagnostic_metadata () : m_cwe (0) {}
void add_cwe (int cwe) { m_cwe = cwe; }
int get_cwe () const { return m_cwe; }
/* Associate R with the diagnostic. R must outlive
the metadata. */
void add_rule (const rule &r)
{
m_rules.safe_push (&r);
}
unsigned get_num_rules () const { return m_rules.length (); }
const rule &get_rule (unsigned idx) const { return *(m_rules[idx]); }
private:
int m_cwe;
auto_vec<const rule *> m_rules;
};
#endif /* ! GCC_DIAGNOSTIC_METADATA_H */

View File

@ -190,6 +190,7 @@ diagnostic_initialize (diagnostic_context *context, int n_opts)
for (i = 0; i < rich_location::STATICALLY_ALLOCATED_RANGES; i++)
context->caret_chars[i] = '^';
context->show_cwe = false;
context->show_rules = false;
context->path_format = DPF_NONE;
context->show_path_depths = false;
context->show_option_requested = false;
@ -1291,6 +1292,51 @@ print_any_cwe (diagnostic_context *context,
}
}
/* If DIAGNOSTIC has any rules associated with it, print them.
For example, if the diagnostic metadata associates it with a rule
named "STR34-C", then " [STR34-C]" will be printed, suitably colorized,
with any URL provided by the rule. */
static void
print_any_rules (diagnostic_context *context,
const diagnostic_info *diagnostic)
{
if (diagnostic->metadata == NULL)
return;
for (unsigned idx = 0; idx < diagnostic->metadata->get_num_rules (); idx++)
{
const diagnostic_metadata::rule &rule
= diagnostic->metadata->get_rule (idx);
if (char *desc = rule.make_description ())
{
pretty_printer *pp = context->printer;
char *saved_prefix = pp_take_prefix (context->printer);
pp_string (pp, " [");
pp_string (pp,
colorize_start (pp_show_color (pp),
diagnostic_kind_color[diagnostic->kind]));
char *url = NULL;
if (pp->url_format != URL_FORMAT_NONE)
{
url = rule.make_url ();
if (url)
pp_begin_url (pp, url);
}
pp_string (pp, desc);
pp_set_prefix (context->printer, saved_prefix);
if (pp->url_format != URL_FORMAT_NONE)
if (url)
pp_end_url (pp);
free (url);
pp_string (pp, colorize_stop (pp_show_color (pp)));
pp_character (pp, ']');
free (desc);
}
}
}
/* Print any metadata about the option used to control DIAGNOSTIC to CONTEXT's
printer, e.g. " [-Werror=uninitialized]".
Subroutine of diagnostic_report_diagnostic. */
@ -1504,6 +1550,8 @@ diagnostic_report_diagnostic (diagnostic_context *context,
pp_output_formatted_text (context->printer);
if (context->show_cwe)
print_any_cwe (context, diagnostic);
if (context->show_rules)
print_any_rules (context, diagnostic);
if (context->show_option_requested)
print_option_information (context, diagnostic, orig_diag_kind);
(*diagnostic_finalizer (context)) (context, diagnostic, orig_diag_kind);

View File

@ -227,6 +227,9 @@ struct diagnostic_context
diagnostics. */
bool show_cwe;
/* True if we should print any rules associated with diagnostics. */
bool show_rules;
/* How should diagnostic_path objects be printed. */
enum diagnostic_path_format path_format;

View File

@ -305,6 +305,7 @@ Objective-C and Objective-C++ Dialects}.
-fno-diagnostics-show-option -fno-diagnostics-show-caret @gol
-fno-diagnostics-show-labels -fno-diagnostics-show-line-numbers @gol
-fno-diagnostics-show-cwe @gol
-fno-diagnostics-show-rule @gol
-fdiagnostics-minimum-margin-width=@var{width} @gol
-fdiagnostics-parseable-fixits -fdiagnostics-generate-patch @gol
-fdiagnostics-show-template-tree -fno-elide-type @gol
@ -5028,6 +5029,15 @@ diagnostics. GCC plugins may also provide diagnostics with such metadata.
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-rules
@opindex fno-diagnostics-show-rules
@opindex fdiagnostics-show-rules
Diagnostic messages can optionally have rules associated with them, such
as from a coding standard, or a specification.
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
@opindex fno-diagnostics-show-line-numbers
@opindex fdiagnostics-show-line-numbers

View File

@ -2876,6 +2876,10 @@ common_handle_option (struct gcc_options *opts,
dc->show_cwe = value;
break;
case OPT_fdiagnostics_show_rules:
dc->show_rules = value;
break;
case OPT_fdiagnostics_path_format_:
dc->path_format = (enum diagnostic_path_format)value;
break;

View File

@ -5,5 +5,5 @@ extern char *gets (char *s);
void test_cwe (void)
{
char buf[1024];
gets (buf); /* { dg-warning "never use 'gets' \\\[CWE-242\\\]" } */
gets (buf); /* { dg-warning "never use 'gets' \\\[CWE-242\\\] \\\[STR34-C\\\]" } */
}

View File

@ -106,9 +106,16 @@ pass_test_metadata::execute (function *fun)
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;
/* CWE-242: Use of Inherently Dangerous Function. */
m.add_cwe (242);
/* Example of a diagnostic_metadata::rule. */
diagnostic_metadata::precanned_rule
test_rule ("STR34-C", "https://example.com/");
m.add_rule (test_rule);
warning_meta (&richloc, m, 0,
"never use %qs", "gets");
}

View File

@ -1038,6 +1038,8 @@ general_init (const char *argv0, bool init_signals)
= global_options_init.x_flag_diagnostics_show_line_numbers;
global_dc->show_cwe
= global_options_init.x_flag_diagnostics_show_cwe;
global_dc->show_rules
= global_options_init.x_flag_diagnostics_show_rules;
global_dc->path_format
= (enum diagnostic_path_format)global_options_init.x_flag_diagnostics_path_format;
global_dc->show_path_depths