aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/Makefile.in8
-rw-r--r--gcc/ada/gcc-interface/misc.cc11
-rw-r--r--gcc/analyzer/checker-path.cc106
-rw-r--r--gcc/analyzer/checker-path.h22
-rw-r--r--gcc/analyzer/pending-diagnostic.h11
-rw-r--r--gcc/analyzer/sm-file.cc14
-rw-r--r--gcc/analyzer/sm-malloc.cc14
-rw-r--r--gcc/analyzer/sm-sensitive.cc9
-rw-r--r--gcc/analyzer/sm-taint.cc11
-rw-r--r--gcc/analyzer/varargs.cc13
-rw-r--r--gcc/c/c-lang.cc12
-rw-r--r--gcc/c/c-tree.h2
-rw-r--r--gcc/common.opt8
-rw-r--r--gcc/cp/cp-lang.cc13
-rw-r--r--gcc/d/d-lang.cc11
-rw-r--r--gcc/diagnostic-client-data-hooks.h105
-rw-r--r--gcc/diagnostic-format-sarif.cc1586
-rw-r--r--gcc/diagnostic-path.h82
-rw-r--r--gcc/diagnostic.cc128
-rw-r--r--gcc/diagnostic.h21
-rw-r--r--gcc/doc/invoke.texi10
-rw-r--r--gcc/doc/sourcebuild.texi6
-rw-r--r--gcc/fortran/f95-lang.cc11
-rw-r--r--gcc/go/go-lang.cc11
-rw-r--r--gcc/langhooks-def.h5
-rw-r--r--gcc/langhooks.cc8
-rw-r--r--gcc/langhooks.h6
-rw-r--r--gcc/logical-location.h72
-rw-r--r--gcc/objc/objc-act.h1
-rw-r--r--gcc/objc/objc-lang.cc8
-rw-r--r--gcc/plugin.cc38
-rw-r--r--gcc/plugin.h3
-rw-r--r--gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-1.c43
-rw-r--r--gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-2.c29
-rw-r--r--gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-3.c30
-rw-r--r--gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-4.c19
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/file-meaning-1.c15
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/malloc-meaning-1.c10
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/malloc-sarif-1.c20
-rw-r--r--gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c15
-rw-r--r--gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-5.c56
-rw-r--r--gcc/testsuite/gcc.dg/plugin/plugin.exp1
-rw-r--r--gcc/testsuite/lib/gcc-dg.exp1
-rw-r--r--gcc/testsuite/lib/scansarif.exp42
-rw-r--r--gcc/tree-diagnostic-client-data-hooks.cc150
-rw-r--r--gcc/tree-diagnostic.cc2
-rw-r--r--gcc/tree-logical-location.cc148
-rw-r--r--gcc/tree-logical-location.h67
48 files changed, 3001 insertions, 13 deletions
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 020b3b1..b6dcc45 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1617,6 +1617,7 @@ OBJS = \
tree-data-ref.o \
tree-dfa.o \
tree-diagnostic.o \
+ tree-diagnostic-client-data-hooks.o \
tree-diagnostic-path.o \
tree-dump.o \
tree-eh.o \
@@ -1625,6 +1626,7 @@ OBJS = \
tree-inline.o \
tree-into-ssa.o \
tree-iterator.o \
+ tree-logical-location.o \
tree-loop-distribution.o \
tree-nested.o \
tree-nrv.o \
@@ -1728,9 +1730,12 @@ OBJS = \
# Objects in libcommon.a, potentially used by all host binaries and with
# no target dependencies.
OBJS-libcommon = diagnostic-spec.o diagnostic.o diagnostic-color.o \
- diagnostic-show-locus.o diagnostic-format-json.o json.o \
+ diagnostic-format-json.o \
+ diagnostic-format-sarif.o \
+ diagnostic-show-locus.o \
edit-context.o \
pretty-print.o intl.o \
+ json.o \
sbitmap.o \
vec.o input.o hash-table.o ggc-none.o memory-block.o \
selftest.o selftest-diagnostic.o sort.o
@@ -2368,6 +2373,7 @@ s-bversion: BASE-VER
$(STAMP) s-bversion
CFLAGS-toplev.o += -DTARGET_NAME=\"$(target_noncanonical)\"
+CFLAGS-tree-diagnostic-client-data-hooks.o += -DTARGET_NAME=\"$(target_noncanonical)\"
CFLAGS-optinfo-emit-json.o += -DTARGET_NAME=\"$(target_noncanonical)\" $(ZLIBINC)
CFLAGS-analyzer/engine.o += $(ZLIBINC)
diff --git a/gcc/ada/gcc-interface/misc.cc b/gcc/ada/gcc-interface/misc.cc
index 7824ebf..f0ca197 100644
--- a/gcc/ada/gcc-interface/misc.cc
+++ b/gcc/ada/gcc-interface/misc.cc
@@ -1292,6 +1292,15 @@ gnat_eh_personality (void)
return gnat_eh_personality_decl;
}
+/* Get a value for the SARIF v2.1.0 "artifact.sourceLanguage" property,
+ based on the list in SARIF v2.1.0 Appendix J. */
+
+static const char *
+gnat_get_sarif_source_language (const char *)
+{
+ return "ada";
+}
+
/* Initialize language-specific bits of tree_contains_struct. */
static void
@@ -1414,6 +1423,8 @@ get_lang_specific (tree node)
#define LANG_HOOKS_DEEP_UNSHARING true
#undef LANG_HOOKS_CUSTOM_FUNCTION_DESCRIPTORS
#define LANG_HOOKS_CUSTOM_FUNCTION_DESCRIPTORS true
+#undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
+#define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE gnat_get_sarif_source_language
struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
diff --git a/gcc/analyzer/checker-path.cc b/gcc/analyzer/checker-path.cc
index 5fdbc38..8aa5bf7 100644
--- a/gcc/analyzer/checker-path.cc
+++ b/gcc/analyzer/checker-path.cc
@@ -112,6 +112,15 @@ event_kind_to_string (enum event_kind ek)
/* class checker_event : public diagnostic_event. */
+/* No-op implementation of diagnostic_event::get_meaning vfunc for
+ checker_event: checker events have no meaning by default. */
+
+diagnostic_event::meaning
+checker_event::get_meaning () const
+{
+ return meaning ();
+}
+
/* Dump this event to PP (for debugging/logging purposes). */
void
@@ -242,6 +251,15 @@ function_entry_event::get_desc (bool can_colorize) const
return make_label_text (can_colorize, "entry to %qE", m_fndecl);
}
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ function entry. */
+
+diagnostic_event::meaning
+function_entry_event::get_meaning () const
+{
+ return meaning (VERB_enter, NOUN_function);
+}
+
/* class state_change_event : public checker_event. */
/* state_change_event's ctor. */
@@ -292,25 +310,33 @@ state_change_event::get_desc (bool can_colorize) const
{
if (flag_analyzer_verbose_state_changes)
{
+ /* Get any "meaning" of event. */
+ diagnostic_event::meaning meaning = get_meaning ();
+ pretty_printer meaning_pp;
+ meaning.dump_to_pp (&meaning_pp);
+
/* Append debug version. */
label_text result;
if (m_origin)
result = make_label_text
(can_colorize,
- "%s (state of %qE: %qs -> %qs, origin: %qE)",
+ "%s (state of %qE: %qs -> %qs, origin: %qE, meaning: %s)",
custom_desc.m_buffer,
var,
m_from->get_name (),
m_to->get_name (),
- origin);
+ origin,
+ pp_formatted_text (&meaning_pp));
else
result = make_label_text
(can_colorize,
- "%s (state of %qE: %qs -> %qs, NULL origin)",
+ "%s (state of %qE: %qs -> %qs, NULL origin, meaning: %s)",
custom_desc.m_buffer,
var,
m_from->get_name (),
- m_to->get_name ());
+ m_to->get_name (),
+ pp_formatted_text (&meaning_pp));
+
custom_desc.maybe_free ();
return result;
}
@@ -357,6 +383,26 @@ state_change_event::get_desc (bool can_colorize) const
}
}
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ state change events: delegate to the pending_diagnostic to
+ get any meaning. */
+
+diagnostic_event::meaning
+state_change_event::get_meaning () const
+{
+ if (m_pending_diagnostic)
+ {
+ region_model *model = m_dst_state.m_region_model;
+ tree var = model->get_representative_tree (m_sval);
+ tree origin = model->get_representative_tree (m_origin);
+ return m_pending_diagnostic->get_meaning_for_state_change
+ (evdesc::state_change (false, var, origin,
+ m_from, m_to, m_emission_id, *this));
+ }
+ else
+ return meaning ();
+}
+
/* class superedge_event : public checker_event. */
/* Get the callgraph_superedge for this superedge_event, which must be
@@ -432,6 +478,21 @@ cfg_edge_event::cfg_edge_event (enum event_kind kind,
gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CFG_EDGE);
}
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ CFG edge events. */
+
+diagnostic_event::meaning
+cfg_edge_event::get_meaning () const
+{
+ const cfg_superedge& cfg_sedge = get_cfg_superedge ();
+ if (cfg_sedge.true_value_p ())
+ return meaning (VERB_branch, PROPERTY_true);
+ else if (cfg_sedge.false_value_p ())
+ return meaning (VERB_branch, PROPERTY_false);
+ else
+ return meaning ();
+}
+
/* class start_cfg_edge_event : public cfg_edge_event. */
/* Implementation of diagnostic_event::get_desc vfunc for
@@ -690,6 +751,15 @@ call_event::get_desc (bool can_colorize) const
get_caller_fndecl ());
}
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ function call events. */
+
+diagnostic_event::meaning
+call_event::get_meaning () const
+{
+ return meaning (VERB_call, NOUN_function);
+}
+
/* Override of checker_event::is_call_p for calls. */
bool
@@ -760,6 +830,15 @@ return_event::get_desc (bool can_colorize) const
m_src_snode->m_fun->decl);
}
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ function return events. */
+
+diagnostic_event::meaning
+return_event::get_meaning () const
+{
+ return meaning (VERB_return, NOUN_function);
+}
+
/* Override of checker_event::is_return_p for returns. */
bool
@@ -778,6 +857,16 @@ start_consolidated_cfg_edges_event::get_desc (bool can_colorize) const
m_edge_sense ? "true" : "false");
}
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ start_consolidated_cfg_edges_event. */
+
+diagnostic_event::meaning
+start_consolidated_cfg_edges_event::get_meaning () const
+{
+ return meaning (VERB_branch,
+ (m_edge_sense ? PROPERTY_true : PROPERTY_false));
+}
+
/* class setjmp_event : public checker_event. */
/* Implementation of diagnostic_event::get_desc vfunc for
@@ -977,6 +1066,15 @@ warning_event::get_desc (bool can_colorize) const
return label_text::borrow ("here");
}
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ warning_event. */
+
+diagnostic_event::meaning
+warning_event::get_meaning () const
+{
+ return meaning (VERB_danger, NOUN_unknown);
+}
+
/* Print a single-line representation of this path to PP. */
void
diff --git a/gcc/analyzer/checker-path.h b/gcc/analyzer/checker-path.h
index fd274e5..8960d56 100644
--- a/gcc/analyzer/checker-path.h
+++ b/gcc/analyzer/checker-path.h
@@ -21,6 +21,8 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_ANALYZER_CHECKER_PATH_H
#define GCC_ANALYZER_CHECKER_PATH_H
+#include "tree-logical-location.h"
+
namespace ana {
/* An enum for discriminating between the concrete subclasses of
@@ -85,7 +87,8 @@ public:
checker_event (enum event_kind kind,
location_t loc, tree fndecl, int depth)
: m_kind (kind), m_loc (loc), m_fndecl (fndecl), m_depth (depth),
- m_pending_diagnostic (NULL), m_emission_id ()
+ m_pending_diagnostic (NULL), m_emission_id (),
+ m_logical_loc (fndecl)
{
}
@@ -94,6 +97,14 @@ public:
location_t get_location () const final override { return m_loc; }
tree get_fndecl () const final override { return m_fndecl; }
int get_stack_depth () const final override { return m_depth; }
+ const logical_location *get_logical_location () const final override
+ {
+ if (m_fndecl)
+ return &m_logical_loc;
+ else
+ return NULL;
+ }
+ meaning get_meaning () const override;
/* Additional functionality. */
@@ -122,6 +133,7 @@ public:
int m_depth;
pending_diagnostic *m_pending_diagnostic;
diagnostic_event_id_t m_emission_id; // only set once all pruning has occurred
+ tree_logical_location m_logical_loc;
};
/* A concrete event subclass for a purely textual event, for use in
@@ -222,6 +234,7 @@ public:
}
label_text get_desc (bool can_colorize) const final override;
+ meaning get_meaning () const override;
bool is_function_entry_p () const final override { return true; }
};
@@ -241,6 +254,7 @@ public:
const program_state &dst_state);
label_text get_desc (bool can_colorize) const final override;
+ meaning get_meaning () const override;
function *get_dest_function () const
{
@@ -295,6 +309,8 @@ public:
class cfg_edge_event : public superedge_event
{
public:
+ meaning get_meaning () const override;
+
const cfg_superedge& get_cfg_superedge () const;
protected:
@@ -353,6 +369,7 @@ public:
location_t loc, tree fndecl, int depth);
label_text get_desc (bool can_colorize) const override;
+ meaning get_meaning () const override;
bool is_call_p () const final override;
@@ -373,6 +390,7 @@ public:
location_t loc, tree fndecl, int depth);
label_text get_desc (bool can_colorize) const final override;
+ meaning get_meaning () const override;
bool is_return_p () const final override;
@@ -394,6 +412,7 @@ public:
}
label_text get_desc (bool can_colorize) const final override;
+ meaning get_meaning () const override;
private:
bool m_edge_sense;
@@ -521,6 +540,7 @@ public:
}
label_text get_desc (bool can_colorize) const final override;
+ meaning get_meaning () const override;
private:
const state_machine *m_sm;
diff --git a/gcc/analyzer/pending-diagnostic.h b/gcc/analyzer/pending-diagnostic.h
index a273f89..9e1c656 100644
--- a/gcc/analyzer/pending-diagnostic.h
+++ b/gcc/analyzer/pending-diagnostic.h
@@ -21,6 +21,8 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_ANALYZER_PENDING_DIAGNOSTIC_H
#define GCC_ANALYZER_PENDING_DIAGNOSTIC_H
+#include "diagnostic-path.h"
+
namespace ana {
/* A bundle of information about things that are of interest to a
@@ -232,6 +234,15 @@ class pending_diagnostic
return label_text ();
}
+ /* Vfunc for implementing diagnostic_event::get_meaning for
+ state_change_event. */
+ virtual diagnostic_event::meaning
+ get_meaning_for_state_change (const evdesc::state_change &) const
+ {
+ /* Default no-op implementation. */
+ return diagnostic_event::meaning ();
+ }
+
/* Precision-of-wording vfunc for describing an interprocedural call
carrying critial state for the diagnostic, from caller to callee.
diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc
index e9b5b8b..8514af1 100644
--- a/gcc/analyzer/sm-file.cc
+++ b/gcc/analyzer/sm-file.cc
@@ -143,6 +143,20 @@ public:
return label_text ();
}
+ diagnostic_event::meaning
+ get_meaning_for_state_change (const evdesc::state_change &change)
+ const final override
+ {
+ if (change.m_old_state == m_sm.get_start_state ()
+ && change.m_new_state == m_sm.m_unchecked)
+ return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
+ diagnostic_event::NOUN_resource);
+ if (change.m_new_state == m_sm.m_closed)
+ return diagnostic_event::meaning (diagnostic_event::VERB_release,
+ diagnostic_event::NOUN_resource);
+ return diagnostic_event::meaning ();
+ }
+
protected:
const fileptr_state_machine &m_sm;
tree m_arg;
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index 3c0f890..3bd4042 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -736,6 +736,20 @@ public:
return label_text ();
}
+ diagnostic_event::meaning
+ get_meaning_for_state_change (const evdesc::state_change &change)
+ const final override
+ {
+ if (change.m_old_state == m_sm.get_start_state ()
+ && unchecked_p (change.m_new_state))
+ return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
+ diagnostic_event::NOUN_memory);
+ if (freed_p (change.m_new_state))
+ return diagnostic_event::meaning (diagnostic_event::VERB_release,
+ diagnostic_event::NOUN_memory);
+ return diagnostic_event::meaning ();
+ }
+
protected:
const malloc_state_machine &m_sm;
tree m_arg;
diff --git a/gcc/analyzer/sm-sensitive.cc b/gcc/analyzer/sm-sensitive.cc
index 20809dd..83c1906 100644
--- a/gcc/analyzer/sm-sensitive.cc
+++ b/gcc/analyzer/sm-sensitive.cc
@@ -117,6 +117,15 @@ public:
return label_text ();
}
+ diagnostic_event::meaning
+ get_meaning_for_state_change (const evdesc::state_change &change)
+ const final override
+ {
+ if (change.m_new_state == m_sm.m_sensitive)
+ return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
+ diagnostic_event::NOUN_sensitive);
+ return diagnostic_event::meaning ();
+ }
label_text describe_call_with_state (const evdesc::call_with_state &info)
final override
{
diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc
index 3aaa69a..d2d03c3 100644
--- a/gcc/analyzer/sm-taint.cc
+++ b/gcc/analyzer/sm-taint.cc
@@ -163,6 +163,17 @@ public:
change.m_expr);
return label_text ();
}
+
+ diagnostic_event::meaning
+ get_meaning_for_state_change (const evdesc::state_change &change)
+ const final override
+ {
+ if (change.m_new_state == m_sm.m_tainted)
+ return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
+ diagnostic_event::NOUN_taint);
+ return diagnostic_event::meaning ();
+ }
+
protected:
const taint_state_machine &m_sm;
tree m_arg;
diff --git a/gcc/analyzer/varargs.cc b/gcc/analyzer/varargs.cc
index 2d27484..846a0b1 100644
--- a/gcc/analyzer/varargs.cc
+++ b/gcc/analyzer/varargs.cc
@@ -335,6 +335,19 @@ public:
return label_text ();
}
+ diagnostic_event::meaning
+ get_meaning_for_state_change (const evdesc::state_change &change)
+ const final override
+ {
+ if (change.m_new_state == m_sm.m_started)
+ return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
+ diagnostic_event::NOUN_resource);
+ if (change.m_new_state == m_sm.m_ended)
+ return diagnostic_event::meaning (diagnostic_event::VERB_release,
+ diagnostic_event::NOUN_resource);
+ return diagnostic_event::meaning ();
+ }
+
protected:
va_list_sm_diagnostic (const va_list_state_machine &sm,
const svalue *ap_sval, tree ap_tree)
diff --git a/gcc/c/c-lang.cc b/gcc/c/c-lang.cc
index eecc0a0..0e67045 100644
--- a/gcc/c/c-lang.cc
+++ b/gcc/c/c-lang.cc
@@ -46,9 +46,21 @@ enum c_language_kind c_language = clk_c;
#undef LANG_HOOKS_GET_SUBSTRING_LOCATION
#define LANG_HOOKS_GET_SUBSTRING_LOCATION c_get_substring_location
+#undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
+#define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE c_get_sarif_source_language
+
/* Each front end provides its own lang hook initializer. */
struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
+/* Get a value for the SARIF v2.1.0 "artifact.sourceLanguage" property,
+ based on the list in SARIF v2.1.0 Appendix J. */
+
+const char *
+c_get_sarif_source_language (const char *)
+{
+ return "c";
+}
+
#if CHECKING_P
namespace selftest {
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 3b322ad..e655afd 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -837,6 +837,8 @@ set_c_expr_source_range (c_expr *expr,
/* In c-fold.cc */
extern vec<tree> incomplete_record_decls;
+extern const char *c_get_sarif_source_language (const char *filename);
+
#if CHECKING_P
namespace selftest {
extern void run_c_tests (void);
diff --git a/gcc/common.opt b/gcc/common.opt
index 3237ce9..7ca0cce 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1390,7 +1390,7 @@ Common Joined RejectNegative UInteger
fdiagnostics-format=
Common Joined RejectNegative Enum(diagnostics_output_format)
--fdiagnostics-format=[text|json|json-stderr|json-file] Select output format.
+-fdiagnostics-format=[text|sarif-stderr|sarif-file|json|json-stderr|json-file] Select output format.
fdiagnostics-escape-format=
Common Joined RejectNegative Enum(diagnostics_escape_format)
@@ -1433,6 +1433,12 @@ Enum(diagnostics_output_format) String(json-stderr) Value(DIAGNOSTICS_OUTPUT_FOR
EnumValue
Enum(diagnostics_output_format) String(json-file) Value(DIAGNOSTICS_OUTPUT_FORMAT_JSON_FILE)
+EnumValue
+Enum(diagnostics_output_format) String(sarif-stderr) Value(DIAGNOSTICS_OUTPUT_FORMAT_SARIF_STDERR)
+
+EnumValue
+Enum(diagnostics_output_format) String(sarif-file) Value(DIAGNOSTICS_OUTPUT_FORMAT_SARIF_FILE)
+
fdiagnostics-parseable-fixits
Common Var(flag_diagnostics_parseable_fixits)
Print fix-it hints in machine-readable form.
diff --git a/gcc/cp/cp-lang.cc b/gcc/cp/cp-lang.cc
index 7c8b947..c3cfde5 100644
--- a/gcc/cp/cp-lang.cc
+++ b/gcc/cp/cp-lang.cc
@@ -36,6 +36,7 @@ static tree get_template_argument_pack_elems_folded (const_tree);
static tree cxx_enum_underlying_base_type (const_tree);
static tree *cxx_omp_get_decl_init (tree);
static void cxx_omp_finish_decl_inits (void);
+static const char *cp_get_sarif_source_language (const char *);
/* Lang hooks common to C++ and ObjC++ are declared in cp/cp-objcp-common.h;
consequently, there should be very few hooks below. */
@@ -100,6 +101,9 @@ static void cxx_omp_finish_decl_inits (void);
#undef LANG_HOOKS_OMP_FINISH_DECL_INITS
#define LANG_HOOKS_OMP_FINISH_DECL_INITS cxx_omp_finish_decl_inits
+#undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
+#define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE cp_get_sarif_source_language
+
/* Each front end provides its own lang hook initializer. */
struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
@@ -265,6 +269,15 @@ cxx_omp_finish_decl_inits (void)
dynamic_initializers = NULL;
}
+/* Get a value for the SARIF v2.1.0 "artifact.sourceLanguage" property,
+ based on the list in SARIF v2.1.0 Appendix J. */
+
+static const char *
+cp_get_sarif_source_language (const char *)
+{
+ return "cplusplus";
+}
+
#if CHECKING_P
namespace selftest {
diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc
index b7c8685..6e4350f 100644
--- a/gcc/d/d-lang.cc
+++ b/gcc/d/d-lang.cc
@@ -1933,6 +1933,15 @@ d_enum_underlying_base_type (const_tree type)
return TREE_TYPE (type);
}
+/* Get a value for the SARIF v2.1.0 "artifact.sourceLanguage" property,
+ based on the list in SARIF v2.1.0 Appendix J. */
+
+static const char *
+d_get_sarif_source_language (const char *)
+{
+ return "d";
+}
+
/* Definitions for our language-specific hooks. */
#undef LANG_HOOKS_NAME
@@ -1966,6 +1975,7 @@ d_enum_underlying_base_type (const_tree type)
#undef LANG_HOOKS_TYPE_FOR_MODE
#undef LANG_HOOKS_TYPE_FOR_SIZE
#undef LANG_HOOKS_TYPE_PROMOTES_TO
+#undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
#define LANG_HOOKS_NAME "GNU D"
#define LANG_HOOKS_INIT d_init
@@ -1998,6 +2008,7 @@ d_enum_underlying_base_type (const_tree type)
#define LANG_HOOKS_TYPE_FOR_MODE d_type_for_mode
#define LANG_HOOKS_TYPE_FOR_SIZE d_type_for_size
#define LANG_HOOKS_TYPE_PROMOTES_TO d_type_promotes_to
+#define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE d_get_sarif_source_language
struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
diff --git a/gcc/diagnostic-client-data-hooks.h b/gcc/diagnostic-client-data-hooks.h
new file mode 100644
index 0000000..ba78546
--- /dev/null
+++ b/gcc/diagnostic-client-data-hooks.h
@@ -0,0 +1,105 @@
+/* Additional metadata about a client for a diagnostic context.
+ Copyright (C) 2022 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_CLIENT_DATA_HOOKS_H
+#define GCC_DIAGNOSTIC_CLIENT_DATA_HOOKS_H
+
+class client_version_info;
+
+/* A bundle of additional metadata, owned by the diagnostic_context,
+ for querying things about the client, like version data. */
+
+class diagnostic_client_data_hooks
+{
+ public:
+ virtual ~diagnostic_client_data_hooks () {}
+
+ /* Get version info for this client, or NULL. */
+ virtual const client_version_info *get_any_version_info () const = 0;
+
+ /* Get the current logical_location for this client, or NULL. */
+ virtual const logical_location *get_current_logical_location () const = 0;
+
+ /* Get a sourceLanguage value for FILENAME, or return NULL.
+ See SARIF v2.1.0 Appendix J for suggested values. */
+ virtual const char *
+ maybe_get_sarif_source_language (const char *filename) const = 0;
+};
+
+/* Factory function for making an instance of diagnostic_client_data_hooks
+ for use in the compiler (i.e. with knowledge of "tree", access to
+ langhooks, etc). */
+
+extern diagnostic_client_data_hooks *make_compiler_data_hooks ();
+
+class diagnostic_client_plugin_info;
+
+/* Abstract base class for a diagnostic_context to get at
+ version information about the client. */
+
+class client_version_info
+{
+public:
+ class plugin_visitor
+ {
+ public:
+ virtual void on_plugin (const diagnostic_client_plugin_info &) = 0;
+ };
+
+ virtual ~client_version_info () {}
+
+ /* Get a string suitable for use as the value of the "name" property
+ (SARIF v2.1.0 section 3.19.8). */
+ virtual const char *get_tool_name () const = 0;
+
+ /* Create a string suitable for use as the value of the "fullName" property
+ (SARIF v2.1.0 section 3.19.9). */
+ virtual char *maybe_make_full_name () const = 0;
+
+ /* Get a string suitable for use as the value of the "version" property
+ (SARIF v2.1.0 section 3.19.13). */
+ virtual const char *get_version_string () const = 0;
+
+ /* Create a string suitable for use as the value of the "informationUri"
+ property (SARIF v2.1.0 section 3.19.17). */
+ virtual char *maybe_make_version_url () const = 0;
+
+ virtual void for_each_plugin (plugin_visitor &v) const = 0;
+};
+
+/* Abstract base class for a diagnostic_context to get at
+ information about a specific plugin within a client. */
+
+class diagnostic_client_plugin_info
+{
+public:
+ /* For use e.g. by SARIF "name" property (SARIF v2.1.0 section 3.19.8). */
+ virtual const char *get_short_name () const = 0;
+
+ /* For use e.g. by SARIF "fullName" property
+ (SARIF v2.1.0 section 3.19.9). */
+ virtual const char *get_full_name () const = 0;
+
+ /* For use e.g. by SARIF "version" property
+ (SARIF v2.1.0 section 3.19.13). */
+ virtual const char *get_version () const = 0;
+};
+
+#endif /* ! GCC_DIAGNOSTIC_CLIENT_DATA_HOOKS_H */
diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc
new file mode 100644
index 0000000..0c33179
--- /dev/null
+++ b/gcc/diagnostic-format-sarif.cc
@@ -0,0 +1,1586 @@
+/* SARIF output for diagnostics
+ Copyright (C) 2018-2022 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/>. */
+
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "diagnostic.h"
+#include "diagnostic-metadata.h"
+#include "diagnostic-path.h"
+#include "json.h"
+#include "cpplib.h"
+#include "logical-location.h"
+#include "diagnostic-client-data-hooks.h"
+
+class sarif_builder;
+
+/* Subclass of json::object for SARIF result objects
+ (SARIF v2.1.0 section 3.27. */
+
+class sarif_result : public json::object
+{
+public:
+ sarif_result () : m_related_locations_arr (NULL) {}
+
+ void
+ on_nested_diagnostic (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind,
+ sarif_builder *builder);
+
+private:
+ json::array *m_related_locations_arr;
+};
+
+/* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
+ and -fdiagnostics-format=sarif-file).
+
+ As diagnostics occur, we build "result" JSON objects, and
+ accumulate state:
+ - which source files are referenced
+ - which warnings are emitted
+ - which CWEs are used
+
+ At the end of the compile, we use the above to build the full SARIF
+ object tree, adding the result objects to the correct place, and
+ creating objects for the various source files, warnings and CWEs
+ referenced.
+
+ Implemented:
+ - fix-it hints
+ - CWE metadata
+ - diagnostic groups (see limitations below)
+ - logical locations (e.g. cfun)
+
+ Known limitations:
+ - GCC supports one-deep nesting of diagnostics (via auto_diagnostic_group),
+ but we only capture location and message information from such nested
+ diagnostics (e.g. we ignore fix-it hints on them)
+ - doesn't yet capture command-line arguments: would be run.invocations
+ property (SARIF v2.1.0 section 3.14.11), as invocation objects
+ (SARIF v2.1.0 section 3.20), but we'd want to capture the arguments to
+ toplev::main, and the response files.
+ - doesn't capture escape_on_output_p
+ - doesn't capture secondary locations within a rich_location
+ (perhaps we should use the "relatedLocations" property: SARIF v2.1.0
+ section 3.27.22)
+ - doesn't capture "artifact.encoding" property
+ (SARIF v2.1.0 section 3.24.9).
+ - doesn't capture hashes of the source files
+ ("artifact.hashes" property (SARIF v2.1.0 section 3.24.11).
+ - doesn't capture the "analysisTarget" property
+ (SARIF v2.1.0 section 3.27.13).
+ - doesn't capture labelled ranges
+ - doesn't capture -Werror cleanly
+ - doesn't capture inlining information (can SARIF handle this?)
+ - doesn't capture macro expansion information (can SARIF handle this?). */
+
+class sarif_builder
+{
+public:
+ sarif_builder (diagnostic_context *context);
+
+ void end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind);
+
+ void end_group ();
+
+ void flush_to_file (FILE *outf);
+
+ json::object *make_location_object (const rich_location &rich_loc,
+ const logical_location *logical_loc);
+ json::object *make_message_object (const char *msg) const;
+
+private:
+ sarif_result *make_result_object (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind);
+ void set_any_logical_locs_arr (json::object *location_obj,
+ const logical_location *logical_loc);
+ json::object *make_location_object (const diagnostic_event &event);
+ json::object *
+ make_logical_location_object (const logical_location &logical_loc) const;
+ json::object *make_code_flow_object (const diagnostic_path &path);
+ json::object *make_thread_flow_object (const diagnostic_path &path);
+ json::object *
+ make_thread_flow_location_object (const diagnostic_event &event);
+ json::array *maybe_make_kinds_array (diagnostic_event::meaning m) const;
+ json::object *maybe_make_physical_location_object (location_t loc);
+ json::object *make_artifact_location_object (location_t loc);
+ json::object *make_artifact_location_object (const char *filename);
+ json::object *make_artifact_location_object_for_pwd () const;
+ json::object *maybe_make_region_object (location_t loc) const;
+ json::object *maybe_make_region_object_for_context (location_t loc) const;
+ json::object *make_region_object_for_hint (const fixit_hint &hint) const;
+ json::object *make_multiformat_message_string (const char *msg) const;
+ json::object *make_top_level_object (json::array *results);
+ json::object *make_run_object (json::array *results);
+ json::object *make_tool_object () const;
+ json::object *make_driver_tool_component_object () const;
+ json::array *maybe_make_taxonomies_array () const;
+ json::object *maybe_make_cwe_taxonomy_object () const;
+ json::object *make_tool_component_reference_object_for_cwe () const;
+ json::object *
+ make_reporting_descriptor_object_for_warning (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind,
+ const char *option_text);
+ json::object *make_reporting_descriptor_object_for_cwe_id (int cwe_id) const;
+ json::object *
+ make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id);
+ json::object *make_artifact_object (const char *filename);
+ json::object *maybe_make_artifact_content_object (const char *filename) const;
+ json::object *maybe_make_artifact_content_object (const char *filename,
+ int start_line,
+ int end_line) const;
+ json::object *make_fix_object (const rich_location &rich_loc);
+ json::object *make_artifact_change_object (const rich_location &richloc);
+ json::object *make_replacement_object (const fixit_hint &hint) const;
+ json::object *make_artifact_content_object (const char *text) const;
+ int get_sarif_column (expanded_location exploc) const;
+
+ diagnostic_context *m_context;
+
+ /* The JSON array of pending diagnostics. */
+ json::array *m_results_array;
+
+ /* The JSON object for the result object (if any) in the current
+ diagnostic group. */
+ sarif_result *m_cur_group_result;
+
+ hash_set <const char *> m_filenames;
+ bool m_seen_any_relative_paths;
+ hash_set <free_string_hash> m_rule_id_set;
+ json::array *m_rules_arr;
+
+ /* The set of all CWE IDs we've seen, if any. */
+ hash_set <int_hash <int, 0, 1> > m_cwe_id_set;
+
+ int m_tabstop;
+};
+
+static sarif_builder *the_builder;
+
+/* class sarif_result : public json::object. */
+
+/* Handle secondary diagnostics that occur within a diagnostic group.
+ The closest SARIF seems to have to nested diagnostics is the
+ "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
+ so we lazily set this property and populate the array if and when
+ secondary diagnostics occur (such as notes to a warning). */
+
+void
+sarif_result::on_nested_diagnostic (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t /*orig_diag_kind*/,
+ sarif_builder *builder)
+{
+ if (!m_related_locations_arr)
+ {
+ m_related_locations_arr = new json::array ();
+ set ("relatedLocations", m_related_locations_arr);
+ }
+
+ /* We don't yet generate meaningful logical locations for notes;
+ sometimes these will related to current_function_decl, but
+ often they won't. */
+ json::object *location_obj
+ = builder->make_location_object (*diagnostic->richloc, NULL);
+ json::object *message_obj
+ = builder->make_message_object (pp_formatted_text (context->printer));
+ pp_clear_output_area (context->printer);
+ location_obj->set ("message", message_obj);
+
+ m_related_locations_arr->append (location_obj);
+}
+
+/* class sarif_builder. */
+
+/* sarif_builder's ctor. */
+
+sarif_builder::sarif_builder (diagnostic_context *context)
+: m_context (context),
+ m_results_array (new json::array ()),
+ m_cur_group_result (NULL),
+ m_seen_any_relative_paths (false),
+ m_rule_id_set (),
+ m_rules_arr (new json::array ()),
+ m_tabstop (context->tabstop)
+{
+}
+
+/* Implementation of "end_diagnostic" for SARIF output. */
+
+void
+sarif_builder::end_diagnostic (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind)
+{
+
+ if (m_cur_group_result)
+ /* Nested diagnostic. */
+ m_cur_group_result->on_nested_diagnostic (context,
+ diagnostic,
+ orig_diag_kind,
+ this);
+ else
+ {
+ /* Top-level diagnostic. */
+ sarif_result *result_obj
+ = make_result_object (context, diagnostic, orig_diag_kind);
+ m_results_array->append (result_obj);
+ m_cur_group_result = result_obj;
+ }
+}
+
+/* Implementation of "end_group_cb" for SARIF output. */
+
+void
+sarif_builder::end_group ()
+{
+ m_cur_group_result = NULL;
+}
+
+/* Create a top-level object, and add it to all the results
+ (and other entities) we've seen so far.
+
+ Flush it all to OUTF. */
+
+void
+sarif_builder::flush_to_file (FILE *outf)
+{
+ json::object *top = make_top_level_object (m_results_array);
+ top->dump (outf);
+ m_results_array = NULL;
+ fprintf (outf, "\n");
+ delete top;
+}
+
+/* Attempt to convert DIAG_KIND to a suitable value for the "level"
+ property (SARIF v2.1.0 section 3.27.10).
+
+ Return NULL if there isn't one. */
+
+static const char *
+maybe_get_sarif_level (diagnostic_t diag_kind)
+{
+ switch (diag_kind)
+ {
+ case DK_WARNING:
+ return "warning";
+ case DK_ERROR:
+ return "error";
+ case DK_NOTE:
+ case DK_ANACHRONISM:
+ return "note";
+ default:
+ return NULL;
+ }
+}
+
+/* Make a string for DIAG_KIND suitable for use a ruleId
+ (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
+ have anything better to use. */
+
+static char *
+make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind)
+{
+ static const char *const diagnostic_kind_text[] = {
+#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
+#include "diagnostic.def"
+#undef DEFINE_DIAGNOSTIC_KIND
+ "must-not-happen"
+ };
+ /* Lose the trailing ": ". */
+ const char *kind_text = diagnostic_kind_text[diag_kind];
+ size_t len = strlen (kind_text);
+ gcc_assert (len > 2);
+ gcc_assert (kind_text[len - 2] == ':');
+ gcc_assert (kind_text[len - 1] == ' ');
+ char *rstrip = xstrdup (kind_text);
+ rstrip[len - 2] = '\0';
+ return rstrip;
+}
+
+/* Make a result object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
+
+sarif_result *
+sarif_builder::make_result_object (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind)
+{
+ sarif_result *result_obj = new sarif_result ();
+
+ /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
+ /* Ideally we'd have an option_name for these. */
+ if (char *option_text
+ = context->option_name (context, diagnostic->option_index,
+ orig_diag_kind, diagnostic->kind))
+ {
+ /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
+ Set ruleId referencing them. */
+ result_obj->set ("ruleId", new json::string (option_text));
+ if (m_rule_id_set.contains (option_text))
+ free (option_text);
+ else
+ {
+ /* This is the first time we've seen this ruleId. */
+ /* Add to set, taking ownership. */
+ m_rule_id_set.add (option_text);
+
+ json::object *reporting_desc_obj
+ = make_reporting_descriptor_object_for_warning (context,
+ diagnostic,
+ orig_diag_kind,
+ option_text);
+ m_rules_arr->append (reporting_desc_obj);
+ }
+ }
+ else
+ {
+ /* Otherwise, we have an "error" or a stray "note"; use the
+ diagnostic kind as the ruleId, so that the result object at least
+ has a ruleId.
+ We don't bother creating reportingDescriptor objects for these. */
+ char *rule_id = make_rule_id_for_diagnostic_kind (orig_diag_kind);
+ result_obj->set ("ruleId", new json::string (rule_id));
+ free (rule_id);
+ }
+
+ /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
+ if (diagnostic->metadata)
+ if (int cwe_id = diagnostic->metadata->get_cwe ())
+ {
+ json::array *taxa_arr = new json::array ();
+ json::object *cwe_id_obj
+ = make_reporting_descriptor_reference_object_for_cwe_id (cwe_id);
+ taxa_arr->append (cwe_id_obj);
+ result_obj->set ("taxa", taxa_arr);
+ }
+
+ /* "level" property (SARIF v2.1.0 section 3.27.10). */
+ if (const char *sarif_level = maybe_get_sarif_level (diagnostic->kind))
+ result_obj->set ("level", new json::string (sarif_level));
+
+ /* "message" property (SARIF v2.1.0 section 3.27.11). */
+ json::object *message_obj
+ = make_message_object (pp_formatted_text (context->printer));
+ pp_clear_output_area (context->printer);
+ result_obj->set ("message", message_obj);
+
+ /* "locations" property (SARIF v2.1.0 section 3.27.12). */
+ json::array *locations_arr = new json::array ();
+ const logical_location *logical_loc = NULL;
+ if (m_context->m_client_data_hooks)
+ logical_loc
+ = m_context->m_client_data_hooks->get_current_logical_location ();
+
+ json::object *location_obj
+ = make_location_object (*diagnostic->richloc, logical_loc);
+ locations_arr->append (location_obj);
+ result_obj->set ("locations", locations_arr);
+
+ /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
+ if (const diagnostic_path *path = diagnostic->richloc->get_path ())
+ {
+ json::array *code_flows_arr = new json::array ();
+ json::object *code_flow_obj = make_code_flow_object (*path);
+ code_flows_arr->append (code_flow_obj);
+ result_obj->set ("codeFlows", code_flows_arr);
+ }
+
+ /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
+ set up later, if any nested diagnostics occur within this diagnostic
+ group. */
+
+ /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
+ const rich_location *richloc = diagnostic->richloc;
+ if (richloc->get_num_fixit_hints ())
+ {
+ json::array *fix_arr = new json::array ();
+ json::object *fix_obj = make_fix_object (*richloc);
+ fix_arr->append (fix_obj);
+ result_obj->set ("fixes", fix_arr);
+ }
+
+ return result_obj;
+}
+
+/* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
+ for a GCC warning. */
+
+json::object *
+sarif_builder::
+make_reporting_descriptor_object_for_warning (diagnostic_context *context,
+ diagnostic_info *diagnostic,
+ diagnostic_t /*orig_diag_kind*/,
+ const char *option_text)
+{
+ json::object *reporting_desc = new json::object ();
+
+ /* "id" property (SARIF v2.1.0 section 3.49.3). */
+ reporting_desc->set ("id", new json::string (option_text));
+
+ /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
+ it seems redundant compared to "id". */
+
+ /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
+ if (context->get_option_url)
+ {
+ char *option_url
+ = context->get_option_url (context, diagnostic->option_index);
+ if (option_url)
+ {
+ reporting_desc->set ("helpUri", new json::string (option_url));
+ free (option_url);
+ }
+ }
+
+ return reporting_desc;
+}
+
+/* Make a reportingDescriptor object (SARIF v2.1.0 section 3.49)
+ for CWE_ID, for use within the CWE taxa array. */
+
+json::object *
+sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
+{
+ json::object *reporting_desc = new json::object ();
+
+ /* "id" property (SARIF v2.1.0 section 3.49.3). */
+ {
+ pretty_printer pp;
+ pp_printf (&pp, "%i", cwe_id);
+ reporting_desc->set ("id", new json::string (pp_formatted_text (&pp)));
+ }
+
+ /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
+ {
+ char *url = get_cwe_url (cwe_id);
+ reporting_desc->set ("helpUri", new json::string (url));
+ free (url);
+ }
+
+ return reporting_desc;
+}
+
+/* Make a reportingDescriptorReference object (SARIF v2.1.0 section 3.52)
+ referencing CWE_ID, for use within a result object.
+ Also, add CWE_ID to m_cwe_id_set. */
+
+json::object *
+sarif_builder::
+make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
+{
+ json::object *desc_ref_obj = new json::object ();
+
+ /* "id" property (SARIF v2.1.0 section 3.52.4). */
+ {
+ pretty_printer pp;
+ pp_printf (&pp, "%i", cwe_id);
+ desc_ref_obj->set ("id", new json::string (pp_formatted_text (&pp)));
+ }
+
+ /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
+ json::object *comp_ref_obj = make_tool_component_reference_object_for_cwe ();
+ desc_ref_obj->set ("toolComponent", comp_ref_obj);
+
+ /* Add CWE_ID to our set. */
+ gcc_assert (cwe_id > 0);
+ m_cwe_id_set.add (cwe_id);
+
+ return desc_ref_obj;
+}
+
+/* Make a toolComponentReference object (SARIF v2.1.0 section 3.54) that
+ references the CWE taxonomy. */
+
+json::object *
+sarif_builder::
+make_tool_component_reference_object_for_cwe () const
+{
+ json::object *comp_ref_obj = new json::object ();
+
+ /* "name" property (SARIF v2.1.0 section 3.54.3). */
+ comp_ref_obj->set ("name", new json::string ("cwe"));
+
+ return comp_ref_obj;
+}
+
+/* If LOGICAL_LOC is non-NULL, use it to create a "logicalLocations" property
+ within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
+
+void
+sarif_builder::
+set_any_logical_locs_arr (json::object *location_obj,
+ const logical_location *logical_loc)
+{
+ if (!logical_loc)
+ return;
+ json::object *logical_loc_obj = make_logical_location_object (*logical_loc);
+ json::array *location_locs_arr = new json::array ();
+ location_locs_arr->append (logical_loc_obj);
+ location_obj->set ("logicalLocations", location_locs_arr);
+}
+
+/* Make a location object (SARIF v2.1.0 section 3.28) for RICH_LOC
+ and LOGICAL_LOC. */
+
+json::object *
+sarif_builder::make_location_object (const rich_location &rich_loc,
+ const logical_location *logical_loc)
+{
+ json::object *location_obj = new json::object ();
+
+ /* Get primary loc from RICH_LOC. */
+ location_t loc = rich_loc.get_loc ();
+
+ /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
+ if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
+ location_obj->set ("physicalLocation", phs_loc_obj);
+
+ /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
+ set_any_logical_locs_arr (location_obj, logical_loc);
+
+ return location_obj;
+}
+
+/* Make a location object (SARIF v2.1.0 section 3.28) for EVENT
+ within a diagnostic_path. */
+
+json::object *
+sarif_builder::make_location_object (const diagnostic_event &event)
+{
+ json::object *location_obj = new json::object ();
+
+ /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
+ location_t loc = event.get_location ();
+ if (json::object *phs_loc_obj = maybe_make_physical_location_object (loc))
+ location_obj->set ("physicalLocation", phs_loc_obj);
+
+ /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
+ const logical_location *logical_loc = event.get_logical_location ();
+ set_any_logical_locs_arr (location_obj, logical_loc);
+
+ /* "message" property (SARIF v2.1.0 section 3.28.5). */
+ label_text ev_desc = event.get_desc (false);
+ json::object *message_obj = make_message_object (ev_desc.m_buffer);
+ location_obj->set ("message", message_obj);
+ ev_desc.maybe_free ();
+
+ return location_obj;
+}
+
+/* Make a physicalLocation object (SARIF v2.1.0 section 3.29) for LOC,
+ or return NULL;
+ Add any filename to the m_artifacts. */
+
+json::object *
+sarif_builder::maybe_make_physical_location_object (location_t loc)
+{
+ if (loc <= BUILTINS_LOCATION)
+ return NULL;
+
+ json::object *phys_loc_obj = new json::object ();
+
+ /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
+ json::object *artifact_loc_obj = make_artifact_location_object (loc);
+ phys_loc_obj->set ("artifactLocation", artifact_loc_obj);
+ m_filenames.add (LOCATION_FILE (loc));
+
+ /* "region" property (SARIF v2.1.0 section 3.29.4). */
+ if (json::object *region_obj = maybe_make_region_object (loc))
+ phys_loc_obj->set ("region", region_obj);
+
+ /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
+ if (json::object *context_region_obj
+ = maybe_make_region_object_for_context (loc))
+ phys_loc_obj->set ("contextRegion", context_region_obj);
+
+ /* Instead, we add artifacts to the run as a whole,
+ with artifact.contents.
+ Could do both, though. */
+
+ return phys_loc_obj;
+}
+
+/* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for LOC,
+ or return NULL. */
+
+json::object *
+sarif_builder::make_artifact_location_object (location_t loc)
+{
+ return make_artifact_location_object (LOCATION_FILE (loc));
+}
+
+/* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
+ for when we need to express paths relative to PWD. */
+
+#define PWD_PROPERTY_NAME ("PWD")
+
+/* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for FILENAME,
+ or return NULL. */
+
+json::object *
+sarif_builder::make_artifact_location_object (const char *filename)
+{
+ json::object *artifact_loc_obj = new json::object ();
+
+ /* "uri" property (SARIF v2.1.0 section 3.4.3). */
+ artifact_loc_obj->set ("uri", new json::string (filename));
+
+ if (filename[0] != '/')
+ {
+ /* If we have a relative path, set the "uriBaseId" property
+ (SARIF v2.1.0 section 3.4.4). */
+ artifact_loc_obj->set ("uriBaseId", new json::string (PWD_PROPERTY_NAME));
+ m_seen_any_relative_paths = true;
+ }
+
+ return artifact_loc_obj;
+}
+
+/* Get the PWD, or NULL, as an absolute file-based URI,
+ adding a trailing forward slash (as required by SARIF v2.1.0
+ section 3.14.14). */
+
+static char *
+make_pwd_uri_str ()
+{
+ /* The prefix of a file-based URI, up to, but not including the path. */
+#define FILE_PREFIX ("file://")
+
+ const char *pwd = getpwd ();
+ if (!pwd)
+ return NULL;
+ size_t len = strlen (pwd);
+ if (len == 0 || pwd[len - 1] != '/')
+ return concat (FILE_PREFIX, pwd, "/", NULL);
+ else
+ {
+ gcc_assert (pwd[len - 1] == '/');
+ return concat (FILE_PREFIX, pwd, NULL);
+ }
+}
+
+/* Make an artifactLocation object (SARIF v2.1.0 section 3.4) for the pwd,
+ for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
+ section 3.14.14) when we have any relative paths. */
+
+json::object *
+sarif_builder::make_artifact_location_object_for_pwd () const
+{
+ json::object *artifact_loc_obj = new json::object ();
+
+ /* "uri" property (SARIF v2.1.0 section 3.4.3). */
+ if (char *pwd = make_pwd_uri_str ())
+ {
+ gcc_assert (strlen (pwd) > 0);
+ gcc_assert (pwd[strlen (pwd) - 1] == '/');
+ artifact_loc_obj->set ("uri", new json::string (pwd));
+ free (pwd);
+ }
+
+ return artifact_loc_obj;
+}
+
+/* Get the column number within EXPLOC. */
+
+int
+sarif_builder::get_sarif_column (expanded_location exploc) const
+{
+ cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
+ return location_compute_display_column (exploc, policy);
+}
+
+/* Make a region object (SARIF v2.1.0 section 3.30) for LOC,
+ or return NULL. */
+
+json::object *
+sarif_builder::maybe_make_region_object (location_t loc) const
+{
+ location_t caret_loc = get_pure_location (loc);
+
+ if (caret_loc <= BUILTINS_LOCATION)
+ return NULL;
+
+ location_t start_loc = get_start (loc);
+ location_t finish_loc = get_finish (loc);
+
+ expanded_location exploc_caret = expand_location (caret_loc);
+ expanded_location exploc_start = expand_location (start_loc);
+ expanded_location exploc_finish = expand_location (finish_loc);
+
+ if (exploc_start.file !=exploc_caret.file)
+ return NULL;
+ if (exploc_finish.file !=exploc_caret.file)
+ return NULL;
+
+ json::object *region_obj = new json::object ();
+
+ /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
+ region_obj->set ("startLine", new json::integer_number (exploc_start.line));
+
+ /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
+ region_obj->set ("startColumn",
+ new json::integer_number (get_sarif_column (exploc_start)));
+
+ /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
+ if (exploc_finish.line != exploc_start.line)
+ region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
+
+ /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
+ This expresses the column immediately beyond the range. */
+ {
+ int next_column = sarif_builder::get_sarif_column (exploc_finish) + 1;
+ region_obj->set ("endColumn", new json::integer_number (next_column));
+ }
+
+ return region_obj;
+}
+
+/* Make a region object (SARIF v2.1.0 section 3.30) for the "contextRegion"
+ property (SARIF v2.1.0 section 3.29.5) of a physicalLocation.
+
+ This is similar to maybe_make_region_object, but ignores column numbers,
+ covering the line(s) as a whole, and including a "snippet" property
+ embedding those source lines, making it easier for consumers to show
+ the pertinent source. */
+
+json::object *
+sarif_builder::maybe_make_region_object_for_context (location_t loc) const
+{
+ location_t caret_loc = get_pure_location (loc);
+
+ if (caret_loc <= BUILTINS_LOCATION)
+ return NULL;
+
+ location_t start_loc = get_start (loc);
+ location_t finish_loc = get_finish (loc);
+
+ expanded_location exploc_caret = expand_location (caret_loc);
+ expanded_location exploc_start = expand_location (start_loc);
+ expanded_location exploc_finish = expand_location (finish_loc);
+
+ if (exploc_start.file !=exploc_caret.file)
+ return NULL;
+ if (exploc_finish.file !=exploc_caret.file)
+ return NULL;
+
+ json::object *region_obj = new json::object ();
+
+ /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
+ region_obj->set ("startLine", new json::integer_number (exploc_start.line));
+
+ /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
+ if (exploc_finish.line != exploc_start.line)
+ region_obj->set ("endLine", new json::integer_number (exploc_finish.line));
+
+ /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
+ if (json::object *artifact_content_obj
+ = maybe_make_artifact_content_object (exploc_start.file,
+ exploc_start.line,
+ exploc_finish.line))
+ region_obj->set ("snippet", artifact_content_obj);
+
+ return region_obj;
+}
+
+/* Make a region object (SARIF v2.1.0 section 3.30) for the deletion region
+ of HINT (as per SARIF v2.1.0 section 3.57.3). */
+
+json::object *
+sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
+{
+ location_t start_loc = hint.get_start_loc ();
+ location_t next_loc = hint.get_next_loc ();
+
+ expanded_location exploc_start = expand_location (start_loc);
+ expanded_location exploc_next = expand_location (next_loc);
+
+ json::object *region_obj = new json::object ();
+
+ /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
+ region_obj->set ("startLine", new json::integer_number (exploc_start.line));
+
+ /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
+ int start_col = get_sarif_column (exploc_start);
+ region_obj->set ("startColumn",
+ new json::integer_number (start_col));
+
+ /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
+ if (exploc_next.line != exploc_start.line)
+ region_obj->set ("endLine", new json::integer_number (exploc_next.line));
+
+ /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
+ This expresses the column immediately beyond the range. */
+ int next_col = get_sarif_column (exploc_next);
+ region_obj->set ("endColumn", new json::integer_number (next_col));
+
+ return region_obj;
+}
+
+/* Attempt to get a string for a logicalLocation's "kind" property
+ (SARIF v2.1.0 section 3.33.7).
+ Return NULL if unknown. */
+
+static const char *
+maybe_get_sarif_kind (enum logical_location_kind kind)
+{
+ switch (kind)
+ {
+ default:
+ gcc_unreachable ();
+ case LOGICAL_LOCATION_KIND_UNKNOWN:
+ return NULL;
+
+ case LOGICAL_LOCATION_KIND_FUNCTION:
+ return "function";
+ case LOGICAL_LOCATION_KIND_MEMBER:
+ return "member";
+ case LOGICAL_LOCATION_KIND_MODULE:
+ return "module";
+ case LOGICAL_LOCATION_KIND_NAMESPACE:
+ return "namespace";
+ case LOGICAL_LOCATION_KIND_TYPE:
+ return "type";
+ case LOGICAL_LOCATION_KIND_RETURN_TYPE:
+ return "returnType";
+ case LOGICAL_LOCATION_KIND_PARAMETER:
+ return "parameter";
+ case LOGICAL_LOCATION_KIND_VARIABLE:
+ return "variable";
+ }
+}
+
+/* Make a logicalLocation object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
+ or return NULL. */
+
+json::object *
+sarif_builder::
+make_logical_location_object (const logical_location &logical_loc) const
+{
+ json::object *logical_loc_obj = new json::object ();
+
+ /* "name" property (SARIF v2.1.0 section 3.33.4). */
+ if (const char *short_name = logical_loc.get_short_name ())
+ logical_loc_obj->set ("name", new json::string (short_name));
+
+ /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
+ if (const char *name_with_scope = logical_loc.get_name_with_scope ())
+ logical_loc_obj->set ("fullyQualifiedName",
+ new json::string (name_with_scope));
+
+ /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
+ if (const char *internal_name = logical_loc.get_internal_name ())
+ logical_loc_obj->set ("decoratedName", new json::string (internal_name));
+
+ /* "kind" property (SARIF v2.1.0 section 3.33.7). */
+ enum logical_location_kind kind = logical_loc.get_kind ();
+ if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
+ logical_loc_obj->set ("kind", new json::string (sarif_kind_str));
+
+ return logical_loc_obj;
+}
+
+/* Make a codeFlow object (SARIF v2.1.0 section 3.36) for PATH. */
+
+json::object *
+sarif_builder::make_code_flow_object (const diagnostic_path &path)
+{
+ json::object *code_flow_obj = new json::object ();
+
+ /* "threadFlows" property (SARIF v2.1.0 section 3.36.3).
+ Currently we only support one thread per result. */
+ json::array *thread_flows_arr = new json::array ();
+ json::object *thread_flow_obj = make_thread_flow_object (path);
+ thread_flows_arr->append (thread_flow_obj);
+ code_flow_obj->set ("threadFlows", thread_flows_arr);
+
+ return code_flow_obj;
+}
+
+/* Make a threadFlow object (SARIF v2.1.0 section 3.37) for PATH. */
+
+json::object *
+sarif_builder::make_thread_flow_object (const diagnostic_path &path)
+{
+ json::object *thread_flow_obj = new json::object ();
+
+ /* "locations" property (SARIF v2.1.0 section 3.37.6). */
+ json::array *locations_arr = new json::array ();
+ for (unsigned i = 0; i < path.num_events (); i++)
+ {
+ const diagnostic_event &event = path.get_event (i);
+ json::object *thread_flow_loc_obj
+ = make_thread_flow_location_object (event);
+ locations_arr->append (thread_flow_loc_obj);
+ }
+ thread_flow_obj->set ("locations", locations_arr);
+
+ return thread_flow_obj;
+}
+
+/* Make a threadFlowLocation object (SARIF v2.1.0 section 3.38) for EVENT. */
+
+json::object *
+sarif_builder::make_thread_flow_location_object (const diagnostic_event &ev)
+{
+ json::object *thread_flow_loc_obj = new json::object ();
+
+ /* "location" property (SARIF v2.1.0 section 3.38.3). */
+ json::object *location_obj = make_location_object (ev);
+ thread_flow_loc_obj->set ("location", location_obj);
+
+ /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
+ diagnostic_event::meaning m = ev.get_meaning ();
+ if (json::array *kinds_arr = maybe_make_kinds_array (m))
+ thread_flow_loc_obj->set ("kinds", kinds_arr);
+
+ /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
+ thread_flow_loc_obj->set ("nestingLevel",
+ new json::integer_number (ev.get_stack_depth ()));
+
+ /* It might be nice to eventually implement the following for -fanalyzer:
+ - the "stack" property (SARIF v2.1.0 section 3.38.5)
+ - the "state" property (SARIF v2.1.0 section 3.38.9)
+ - the "importance" property (SARIF v2.1.0 section 3.38.13). */
+
+ return thread_flow_loc_obj;
+}
+
+/* If M has any known meaning, make a json array suitable for the "kinds"
+ property of a threadFlowLocation object (SARIF v2.1.0 section 3.38.8).
+
+ Otherwise, return NULL. */
+
+json::array *
+sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const
+{
+ if (m.m_verb == diagnostic_event::VERB_unknown
+ && m.m_noun == diagnostic_event::NOUN_unknown
+ && m.m_property == diagnostic_event::PROPERTY_unknown)
+ return NULL;
+
+ json::array *kinds_arr = new json::array ();
+ if (const char *verb_str
+ = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb))
+ kinds_arr->append (new json::string (verb_str));
+ if (const char *noun_str
+ = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun))
+ kinds_arr->append (new json::string (noun_str));
+ if (const char *property_str
+ = diagnostic_event::meaning::maybe_get_property_str (m.m_property))
+ kinds_arr->append (new json::string (property_str));
+ return kinds_arr;
+}
+
+/* Make a message object (SARIF v2.1.0 section 3.11) for MSG. */
+
+json::object *
+sarif_builder::make_message_object (const char *msg) const
+{
+ json::object *message_obj = new json::object ();
+
+ /* "text" property (SARIF v2.1.0 section 3.11.8). */
+ message_obj->set ("text", new json::string (msg));
+
+ return message_obj;
+}
+
+/* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12)
+ for MSG. */
+
+json::object *
+sarif_builder::make_multiformat_message_string (const char *msg) const
+{
+ json::object *message_obj = new json::object ();
+
+ /* "text" property (SARIF v2.1.0 section 3.12.3). */
+ message_obj->set ("text", new json::string (msg));
+
+ return message_obj;
+}
+
+#define SARIF_SCHEMA "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"
+#define SARIF_VERSION "2.1.0"
+
+/* Make a top-level sarifLog object (SARIF v2.1.0 section 3.13).
+ Take ownership of RESULTS. */
+
+json::object *
+sarif_builder::make_top_level_object (json::array *results)
+{
+ json::object *log_obj = new json::object ();
+
+ /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
+ log_obj->set ("$schema", new json::string (SARIF_SCHEMA));
+
+ /* "version" property (SARIF v2.1.0 section 3.13.2). */
+ log_obj->set ("version", new json::string (SARIF_VERSION));
+
+ /* "runs" property (SARIF v2.1.0 section 3.13.4). */
+ json::array *run_arr = new json::array ();
+ json::object *run_obj = make_run_object (results);
+ run_arr->append (run_obj);
+ log_obj->set ("runs", run_arr);
+
+ return log_obj;
+}
+
+/* Make a run object (SARIF v2.1.0 section 3.14).
+ Take ownership of RESULTS. */
+
+json::object *
+sarif_builder::make_run_object (json::array *results)
+{
+ json::object *run_obj = new json::object ();
+
+ /* "tool" property (SARIF v2.1.0 section 3.14.6). */
+ json::object *tool_obj = make_tool_object ();
+ run_obj->set ("tool", tool_obj);
+
+ /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
+ if (json::array *taxonomies_arr = maybe_make_taxonomies_array ())
+ run_obj->set ("taxonomies", taxonomies_arr);
+
+ /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
+ if (m_seen_any_relative_paths)
+ {
+ json::object *orig_uri_base_ids = new json::object ();
+ run_obj->set ("originalUriBaseIds", orig_uri_base_ids);
+ json::object *pwd_art_loc_obj = make_artifact_location_object_for_pwd ();
+ orig_uri_base_ids->set (PWD_PROPERTY_NAME, pwd_art_loc_obj);
+ }
+
+ /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
+ json::array *artifacts_arr = new json::array ();
+ for (auto iter : m_filenames)
+ {
+ json::object *artifact_obj = make_artifact_object (iter);
+ artifacts_arr->append (artifact_obj);
+ }
+ run_obj->set ("artifacts", artifacts_arr);
+
+ /* "results" property (SARIF v2.1.0 section 3.14.23). */
+ run_obj->set ("results", results);
+
+ return run_obj;
+}
+
+/* Make a tool object (SARIF v2.1.0 section 3.18). */
+
+json::object *
+sarif_builder::make_tool_object () const
+{
+ json::object *tool_obj = new json::object ();
+
+ /* "driver" property (SARIF v2.1.0 section 3.18.2). */
+ json::object *driver_obj = make_driver_tool_component_object ();
+ tool_obj->set ("driver", driver_obj);
+
+ /* Report plugins via the "extensions" property
+ (SARIF v2.1.0 section 3.18.3). */
+ if (m_context->m_client_data_hooks)
+ if (const client_version_info *vinfo
+ = m_context->m_client_data_hooks->get_any_version_info ())
+ {
+ class my_plugin_visitor : public client_version_info :: plugin_visitor
+ {
+ public:
+ void on_plugin (const diagnostic_client_plugin_info &p) final override
+ {
+ /* Create a toolComponent object (SARIF v2.1.0 section 3.19)
+ for the plugin. */
+ json::object *plugin_obj = new json::object ();
+ m_plugin_objs.safe_push (plugin_obj);
+
+ /* "name" property (SARIF v2.1.0 section 3.19.8). */
+ if (const char *short_name = p.get_short_name ())
+ plugin_obj->set ("name", new json::string (short_name));
+
+ /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
+ if (const char *full_name = p.get_full_name ())
+ plugin_obj->set ("fullName", new json::string (full_name));
+
+ /* "version" property (SARIF v2.1.0 section 3.19.13). */
+ if (const char *version = p.get_version ())
+ plugin_obj->set ("version", new json::string (version));
+ }
+ auto_vec <json::object *> m_plugin_objs;
+ };
+ my_plugin_visitor v;
+ vinfo->for_each_plugin (v);
+ if (v.m_plugin_objs.length () > 0)
+ {
+ json::array *extensions_arr = new json::array ();
+ tool_obj->set ("extensions", extensions_arr);
+ for (auto iter : v.m_plugin_objs)
+ extensions_arr->append (iter);
+ }
+ }
+
+ /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
+ "extensions" (see toplev.cc: print_version). */
+
+ return tool_obj;
+}
+
+/* Make a toolComponent object (SARIF v2.1.0 section 3.19) for what SARIF
+ calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
+
+json::object *
+sarif_builder::make_driver_tool_component_object () const
+{
+ json::object *driver_obj = new json::object ();
+
+ if (m_context->m_client_data_hooks)
+ if (const client_version_info *vinfo
+ = m_context->m_client_data_hooks->get_any_version_info ())
+ {
+ /* "name" property (SARIF v2.1.0 section 3.19.8). */
+ if (const char *name = vinfo->get_tool_name ())
+ driver_obj->set ("name", new json::string (name));
+
+ /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
+ if (char *full_name = vinfo->maybe_make_full_name ())
+ {
+ driver_obj->set ("fullName", new json::string (full_name));
+ free (full_name);
+ }
+
+ /* "version" property (SARIF v2.1.0 section 3.19.13). */
+ if (const char *version = vinfo->get_version_string ())
+ driver_obj->set ("version", new json::string (version));
+
+ /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
+ if (char *version_url = vinfo->maybe_make_version_url ())
+ {
+ driver_obj->set ("informationUri", new json::string (version_url));
+ free (version_url);
+ }
+ }
+
+ /* "rules" property (SARIF v2.1.0 section 3.19.23). */
+ driver_obj->set ("rules", m_rules_arr);
+
+ return driver_obj;
+}
+
+/* If we've seen any CWE IDs, make an array for the "taxonomies" property
+ (SARIF v2.1.0 section 3.14.8) of a run object, containting a singl
+ toolComponent (3.19) as per 3.19.3, representing the CWE.
+
+ Otherwise return NULL. */
+
+json::array *
+sarif_builder::maybe_make_taxonomies_array () const
+{
+ json::object *cwe_obj = maybe_make_cwe_taxonomy_object ();
+ if (!cwe_obj)
+ return NULL;
+
+ /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
+ json::array *taxonomies_arr = new json::array ();
+ taxonomies_arr->append (cwe_obj);
+ return taxonomies_arr;
+}
+
+/* If we've seen any CWE IDs, make a toolComponent object
+ (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
+ Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
+
+ Otherwise return NULL. */
+
+json::object *
+sarif_builder::maybe_make_cwe_taxonomy_object () const
+{
+ if (m_cwe_id_set.is_empty ())
+ return NULL;
+
+ json::object *taxonomy_obj = new json::object ();
+
+ /* "name" property (SARIF v2.1.0 section 3.19.8). */
+ taxonomy_obj->set ("name", new json::string ("CWE"));
+
+ /* "version" property (SARIF v2.1.0 section 3.19.13). */
+ taxonomy_obj->set ("version", new json::string ("4.7"));
+
+ /* "organization" property (SARIF v2.1.0 section 3.19.18). */
+ taxonomy_obj->set ("organization", new json::string ("MITRE"));
+
+ /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
+ json::object *short_desc
+ = make_multiformat_message_string ("The MITRE"
+ " Common Weakness Enumeration");
+ taxonomy_obj->set ("shortDescription", short_desc);
+
+ /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
+ json::array *taxa_arr = new json::array ();
+ for (auto cwe_id : m_cwe_id_set)
+ {
+ json::object *cwe_taxon
+ = make_reporting_descriptor_object_for_cwe_id (cwe_id);
+ taxa_arr->append (cwe_taxon);
+ }
+ taxonomy_obj->set ("taxa", taxa_arr);
+
+ return taxonomy_obj;
+}
+
+/* Make an artifact object (SARIF v2.1.0 section 3.24). */
+
+json::object *
+sarif_builder::make_artifact_object (const char *filename)
+{
+ json::object *artifact_obj = new json::object ();
+
+ /* "location" property (SARIF v2.1.0 section 3.24.2). */
+ json::object *artifact_loc_obj = make_artifact_location_object (filename);
+ artifact_obj->set ("location", artifact_loc_obj);
+
+ /* "contents" property (SARIF v2.1.0 section 3.24.8). */
+ if (json::object *artifact_content_obj
+ = maybe_make_artifact_content_object (filename))
+ artifact_obj->set ("contents", artifact_content_obj);
+
+ /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
+ if (m_context->m_client_data_hooks)
+ if (const char *source_lang
+ = m_context->m_client_data_hooks->maybe_get_sarif_source_language
+ (filename))
+ artifact_obj->set ("sourceLanguage", new json::string (source_lang));
+
+ return artifact_obj;
+}
+
+/* Read all data from F_IN until EOF.
+ Return a NULL-terminated buffer containing the data, which must be
+ freed by the caller.
+ Return NULL on errors. */
+
+static char *
+read_until_eof (FILE *f_in)
+{
+ /* Read content, allocating a buffer for it. */
+ char *result = NULL;
+ size_t total_sz = 0;
+ size_t alloc_sz = 0;
+ char buf[4096];
+ size_t iter_sz_in;
+
+ while ( (iter_sz_in = fread (buf, 1, sizeof (buf), f_in)) )
+ {
+ gcc_assert (alloc_sz >= total_sz);
+ size_t old_total_sz = total_sz;
+ total_sz += iter_sz_in;
+ /* Allow 1 extra byte for 0-termination. */
+ if (alloc_sz < (total_sz + 1))
+ {
+ size_t new_alloc_sz = alloc_sz ? alloc_sz * 2: total_sz + 1;
+ result = (char *)xrealloc (result, new_alloc_sz);
+ alloc_sz = new_alloc_sz;
+ }
+ memcpy (result + old_total_sz, buf, iter_sz_in);
+ }
+
+ if (!feof (f_in))
+ return NULL;
+
+ /* 0-terminate the buffer. */
+ gcc_assert (total_sz < alloc_sz);
+ result[total_sz] = '\0';
+
+ return result;
+}
+
+/* Read all data from FILENAME until EOF.
+ Return a NULL-terminated buffer containing the data, which must be
+ freed by the caller.
+ Return NULL on errors. */
+
+static char *
+maybe_read_file (const char *filename)
+{
+ FILE *f_in = fopen (filename, "r");
+ if (!f_in)
+ return NULL;
+ char *result = read_until_eof (f_in);
+ fclose (f_in);
+ return result;
+}
+
+/* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the
+ full contents of FILENAME. */
+
+json::object *
+sarif_builder::maybe_make_artifact_content_object (const char *filename) const
+{
+ char *text_utf8 = maybe_read_file (filename);
+ if (!text_utf8)
+ return NULL;
+
+ json::object *artifact_content_obj = new json::object ();
+ artifact_content_obj->set ("text", new json::string (text_utf8));
+ free (text_utf8);
+
+ return artifact_content_obj;
+}
+
+/* Attempt to read the given range of lines from FILENAME; return
+ a freshly-allocated 0-terminated buffer containing them, or NULL. */
+
+static char *
+get_source_lines (const char *filename,
+ int start_line,
+ int end_line)
+{
+ auto_vec<char> result;
+
+ for (int line = start_line; line <= end_line; line++)
+ {
+ char_span line_content = location_get_source_line (filename, line);
+ if (!line_content.get_buffer ())
+ return NULL;
+ result.reserve (line_content.length () + 1);
+ for (size_t i = 0; i < line_content.length (); i++)
+ result.quick_push (line_content[i]);
+ result.quick_push ('\n');
+ }
+ result.safe_push ('\0');
+
+ return xstrdup (result.address ());
+}
+
+/* Make an artifactContent object (SARIF v2.1.0 section 3.3) for the given
+ run of lines within FILENAME (including the endpoints). */
+
+json::object *
+sarif_builder::maybe_make_artifact_content_object (const char *filename,
+ int start_line,
+ int end_line) const
+{
+ char *text_utf8 = get_source_lines (filename, start_line, end_line);
+
+ if (!text_utf8)
+ return NULL;
+
+ json::object *artifact_content_obj = new json::object ();
+ artifact_content_obj->set ("text", new json::string (text_utf8));
+ free (text_utf8);
+
+ return artifact_content_obj;
+}
+
+/* Make a fix object (SARIF v2.1.0 section 3.55) for RICHLOC. */
+
+json::object *
+sarif_builder::make_fix_object (const rich_location &richloc)
+{
+ json::object *fix_obj = new json::object ();
+
+ /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
+ /* We assume that all fix-it hints in RICHLOC affect the same file. */
+ json::array *artifact_change_arr = new json::array ();
+ json::object *artifact_change_obj = make_artifact_change_object (richloc);
+ artifact_change_arr->append (artifact_change_obj);
+ fix_obj->set ("artifactChanges", artifact_change_arr);
+
+ return fix_obj;
+}
+
+/* Make an artifactChange object (SARIF v2.1.0 section 3.56) for RICHLOC. */
+
+json::object *
+sarif_builder::make_artifact_change_object (const rich_location &richloc)
+{
+ json::object *artifact_change_obj = new json::object ();
+
+ /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
+ json::object *artifact_location_obj
+ = make_artifact_location_object (richloc.get_loc ());
+ artifact_change_obj->set ("artifactLocation", artifact_location_obj);
+
+ /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
+ json::array *replacement_arr = new json::array ();
+ for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
+ {
+ const fixit_hint *hint = richloc.get_fixit_hint (i);
+ json::object *replacement_obj = make_replacement_object (*hint);
+ replacement_arr->append (replacement_obj);
+ }
+ artifact_change_obj->set ("replacements", replacement_arr);
+
+ return artifact_change_obj;
+}
+
+/* Make a replacement object (SARIF v2.1.0 section 3.57) for HINT. */
+
+json::object *
+sarif_builder::make_replacement_object (const fixit_hint &hint) const
+{
+ json::object *replacement_obj = new json::object ();
+
+ /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
+ json::object *region_obj = make_region_object_for_hint (hint);
+ replacement_obj->set ("deletedRegion", region_obj);
+
+ /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
+ json::object *content_obj = make_artifact_content_object (hint.get_string ());
+ replacement_obj->set ("insertedContent", content_obj);
+
+ return replacement_obj;
+}
+
+/* Make an artifactContent object (SARIF v2.1.0 section 3.3) for TEXT. */
+
+json::object *
+sarif_builder::make_artifact_content_object (const char *text) const
+{
+ json::object *content_obj = new json::object ();
+
+ /* "text" property (SARIF v2.1.0 section 3.3.2). */
+ content_obj->set ("text", new json::string (text));
+
+ return content_obj;
+}
+
+/* No-op implementation of "begin_diagnostic" for SARIF output. */
+
+static void
+sarif_begin_diagnostic (diagnostic_context *, diagnostic_info *)
+{
+}
+
+/* Implementation of "end_diagnostic" for SARIF output. */
+
+static void
+sarif_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
+ diagnostic_t orig_diag_kind)
+{
+ gcc_assert (the_builder);
+ the_builder->end_diagnostic (context, diagnostic, orig_diag_kind);
+}
+
+/* No-op implementation of "begin_group_cb" for SARIF output. */
+
+static void
+sarif_begin_group (diagnostic_context *)
+{
+}
+
+/* Implementation of "end_group_cb" for SARIF output. */
+
+static void
+sarif_end_group (diagnostic_context *)
+{
+ gcc_assert (the_builder);
+ the_builder->end_group ();
+}
+
+/* Flush the top-level array to OUTF. */
+
+static void
+sarif_flush_to_file (FILE *outf)
+{
+ gcc_assert (the_builder);
+ the_builder->flush_to_file (outf);
+ delete the_builder;
+ the_builder = NULL;
+}
+
+/* Callback for final cleanup for SARIF output to stderr. */
+
+static void
+sarif_stderr_final_cb (diagnostic_context *)
+{
+ gcc_assert (the_builder);
+ sarif_flush_to_file (stderr);
+}
+
+static char *sarif_output_base_file_name;
+
+/* Callback for final cleanup for SARIF output to a file. */
+
+static void
+sarif_file_final_cb (diagnostic_context *)
+{
+ char *filename = concat (sarif_output_base_file_name, ".sarif", NULL);
+ FILE *outf = fopen (filename, "w");
+ if (!outf)
+ {
+ const char *errstr = xstrerror (errno);
+ fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
+ filename, errstr);
+ free (filename);
+ return;
+ }
+ gcc_assert (the_builder);
+ sarif_flush_to_file (outf);
+ fclose (outf);
+ free (filename);
+}
+
+/* Populate CONTEXT in preparation for SARIF output (either to stderr, or
+ to a file). */
+
+static void
+diagnostic_output_format_init_sarif (diagnostic_context *context)
+{
+ the_builder = new sarif_builder (context);
+
+ /* Override callbacks. */
+ context->begin_diagnostic = sarif_begin_diagnostic;
+ context->end_diagnostic = sarif_end_diagnostic;
+ context->begin_group_cb = sarif_begin_group;
+ context->end_group_cb = sarif_end_group;
+ context->print_path = NULL; /* handled in sarif_end_diagnostic. */
+
+ /* The metadata is handled in SARIF format, rather than as text. */
+ context->show_cwe = false;
+
+ /* The option is handled in SARIF format, rather than as text. */
+ context->show_option_requested = false;
+
+ /* Don't colorize the text. */
+ pp_show_color (context->printer) = false;
+}
+
+/* Populate CONTEXT in preparation for SARIF output to stderr. */
+
+void
+diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
+{
+ diagnostic_output_format_init_sarif (context);
+ context->final_cb = sarif_stderr_final_cb;
+}
+
+/* Populate CONTEXT in preparation for SARIF output to a file named
+ BASE_FILE_NAME.sarif. */
+
+void
+diagnostic_output_format_init_sarif_file (diagnostic_context *context,
+ const char *base_file_name)
+{
+ diagnostic_output_format_init_sarif (context);
+ context->final_cb = sarif_file_final_cb;
+ sarif_output_base_file_name = xstrdup (base_file_name);
+}
diff --git a/gcc/diagnostic-path.h b/gcc/diagnostic-path.h
index 6c1190d..8ce4ff7 100644
--- a/gcc/diagnostic-path.h
+++ b/gcc/diagnostic-path.h
@@ -69,6 +69,75 @@ along with GCC; see the file COPYING3. If not see
class diagnostic_event
{
public:
+ /* Enums for giving a sense of what this event means.
+ Roughly corresponds to SARIF v2.1.0 section 3.38.8. */
+ enum verb
+ {
+ VERB_unknown,
+
+ VERB_acquire,
+ VERB_release,
+ VERB_enter,
+ VERB_exit,
+ VERB_call,
+ VERB_return,
+ VERB_branch,
+
+ VERB_danger
+ };
+ enum noun
+ {
+ NOUN_unknown,
+
+ NOUN_taint,
+ NOUN_sensitive, // this one isn't in SARIF v2.1.0; filed as https://github.com/oasis-tcs/sarif-spec/issues/530
+ NOUN_function,
+ NOUN_lock,
+ NOUN_memory,
+ NOUN_resource
+ };
+ enum property
+ {
+ PROPERTY_unknown,
+
+ PROPERTY_true,
+ PROPERTY_false
+ };
+ /* A bundle of such enums, allowing for descriptions of the meaning of
+ an event, such as
+ - "acquire memory": meaning (VERB_acquire, NOUN_memory)
+ - "take true branch"": meaning (VERB_branch, PROPERTY_true)
+ - "return from function": meaning (VERB_return, NOUN_function)
+ etc, as per SARIF's threadFlowLocation "kinds" property
+ (SARIF v2.1.0 section 3.38.8). */
+ struct meaning
+ {
+ meaning ()
+ : m_verb (VERB_unknown),
+ m_noun (NOUN_unknown),
+ m_property (PROPERTY_unknown)
+ {
+ }
+ meaning (enum verb verb, enum noun noun)
+ : m_verb (verb), m_noun (noun), m_property (PROPERTY_unknown)
+ {
+ }
+ meaning (enum verb verb, enum property property)
+ : m_verb (verb), m_noun (NOUN_unknown), m_property (property)
+ {
+ }
+
+ void dump_to_pp (pretty_printer *pp) const;
+
+ static const char *maybe_get_verb_str (enum verb);
+ static const char *maybe_get_noun_str (enum noun);
+ static const char *maybe_get_property_str (enum property);
+
+ enum verb m_verb;
+ enum noun m_noun;
+ enum property m_property;
+ };
+
virtual ~diagnostic_event () {}
virtual location_t get_location () const = 0;
@@ -81,6 +150,11 @@ class diagnostic_event
/* Get a localized (and possibly colorized) description of this event. */
virtual label_text get_desc (bool can_colorize) const = 0;
+
+ /* Get a logical_location for this event, or NULL. */
+ virtual const logical_location *get_logical_location () const = 0;
+
+ virtual meaning get_meaning () const = 0;
};
/* Abstract base class for getting at a sequence of events. */
@@ -113,6 +187,14 @@ class simple_diagnostic_event : public diagnostic_event
{
return label_text::borrow (m_desc);
}
+ const logical_location *get_logical_location () const final override
+ {
+ return NULL;
+ }
+ meaning get_meaning () const final override
+ {
+ return meaning ();
+ }
private:
location_t m_loc;
diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
index 2550483..f2a82ff 100644
--- a/gcc/diagnostic.cc
+++ b/gcc/diagnostic.cc
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic-url.h"
#include "diagnostic-metadata.h"
#include "diagnostic-path.h"
+#include "diagnostic-client-data-hooks.h"
#include "edit-context.h"
#include "selftest.h"
#include "selftest-diagnostic.h"
@@ -240,6 +241,7 @@ diagnostic_initialize (diagnostic_context *context, int n_opts)
context->end_group_cb = NULL;
context->final_cb = default_diagnostic_final_cb;
context->includes_seen = NULL;
+ context->m_client_data_hooks = NULL;
}
/* Maybe initialize the color support. We require clients to do this
@@ -338,6 +340,12 @@ diagnostic_finish (diagnostic_context *context)
delete context->includes_seen;
context->includes_seen = nullptr;
}
+
+ if (context->m_client_data_hooks)
+ {
+ delete context->m_client_data_hooks;
+ context->m_client_data_hooks = NULL;
+ }
}
/* Initialize DIAGNOSTIC, where the message MSG has already been
@@ -820,6 +828,116 @@ diagnostic_show_any_path (diagnostic_context *context,
context->print_path (context, path);
}
+/* class diagnostic_event. */
+
+/* struct diagnostic_event::meaning. */
+
+void
+diagnostic_event::meaning::dump_to_pp (pretty_printer *pp) const
+{
+ bool need_comma = false;
+ pp_character (pp, '{');
+ if (const char *verb_str = maybe_get_verb_str (m_verb))
+ {
+ pp_printf (pp, "verb: %qs", verb_str);
+ need_comma = true;
+ }
+ if (const char *noun_str = maybe_get_noun_str (m_noun))
+ {
+ if (need_comma)
+ pp_string (pp, ", ");
+ pp_printf (pp, "noun: %qs", noun_str);
+ need_comma = true;
+ }
+ if (const char *property_str = maybe_get_property_str (m_property))
+ {
+ if (need_comma)
+ pp_string (pp, ", ");
+ pp_printf (pp, "property: %qs", property_str);
+ need_comma = true;
+ }
+ pp_character (pp, '}');
+}
+
+/* Get a string (or NULL) for V suitable for use within a SARIF
+ threadFlowLocation "kinds" property (SARIF v2.1.0 section 3.38.8). */
+
+const char *
+diagnostic_event::meaning::maybe_get_verb_str (enum verb v)
+{
+ switch (v)
+ {
+ default:
+ gcc_unreachable ();
+ case VERB_unknown:
+ return NULL;
+ case VERB_acquire:
+ return "acquire";
+ case VERB_release:
+ return "release";
+ case VERB_enter:
+ return "enter";
+ case VERB_exit:
+ return "exit";
+ case VERB_call:
+ return "call";
+ case VERB_return:
+ return "return";
+ case VERB_branch:
+ return "branch";
+ case VERB_danger:
+ return "danger";
+ }
+}
+
+/* Get a string (or NULL) for N suitable for use within a SARIF
+ threadFlowLocation "kinds" property (SARIF v2.1.0 section 3.38.8). */
+
+const char *
+diagnostic_event::meaning::maybe_get_noun_str (enum noun n)
+{
+ switch (n)
+ {
+ default:
+ gcc_unreachable ();
+ case NOUN_unknown:
+ return NULL;
+ case NOUN_taint:
+ return "taint";
+ case NOUN_sensitive:
+ return "sensitive";
+ case NOUN_function:
+ return "function";
+ case NOUN_lock:
+ return "lock";
+ case NOUN_memory:
+ return "memory";
+ case NOUN_resource:
+ return "resource";
+ }
+}
+
+/* Get a string (or NULL) for P suitable for use within a SARIF
+ threadFlowLocation "kinds" property (SARIF v2.1.0 section 3.38.8). */
+
+const char *
+diagnostic_event::meaning::maybe_get_property_str (enum property p)
+{
+ switch (p)
+ {
+ default:
+ gcc_unreachable ();
+ case PROPERTY_unknown:
+ return NULL;
+ case PROPERTY_true:
+ return "true";
+ case PROPERTY_false:
+ return "false";
+ }
+}
+
+/* class diagnostic_path. */
+
/* Return true if the events in this path involve more than one
function, or false if it is purely intraprocedural. */
@@ -1131,7 +1249,7 @@ update_effective_level_from_pragmas (diagnostic_context *context,
/* Generate a URL string describing CWE. The caller is responsible for
freeing the string. */
-static char *
+char *
get_cwe_url (int cwe)
{
return xasprintf ("https://cwe.mitre.org/data/definitions/%i.html", cwe);
@@ -2095,6 +2213,14 @@ diagnostic_output_format_init (diagnostic_context *context,
case DIAGNOSTICS_OUTPUT_FORMAT_JSON_FILE:
diagnostic_output_format_init_json_file (context, base_file_name);
break;
+
+ case DIAGNOSTICS_OUTPUT_FORMAT_SARIF_STDERR:
+ diagnostic_output_format_init_sarif_stderr (context);
+ break;
+
+ case DIAGNOSTICS_OUTPUT_FORMAT_SARIF_FILE:
+ diagnostic_output_format_init_sarif_file (context, base_file_name);
+ break;
}
}
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index dd3af03..96c9a72 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -63,7 +63,13 @@ enum diagnostics_output_format
DIAGNOSTICS_OUTPUT_FORMAT_JSON_STDERR,
/* JSON-based output, to a file. */
- DIAGNOSTICS_OUTPUT_FORMAT_JSON_FILE
+ DIAGNOSTICS_OUTPUT_FORMAT_JSON_FILE,
+
+ /* SARIF-based output, to stderr. */
+ DIAGNOSTICS_OUTPUT_FORMAT_SARIF_STDERR,
+
+ /* SARIF-based output, to a file. */
+ DIAGNOSTICS_OUTPUT_FORMAT_SARIF_FILE
};
/* An enum for controlling how diagnostic_paths should be printed. */
@@ -162,6 +168,8 @@ typedef void (*diagnostic_finalizer_fn) (diagnostic_context *,
class edit_context;
namespace json { class value; }
+class diagnostic_client_data_hooks;
+class logical_location;
/* This data structure bundles altogether any information relevant to
the context of a diagnostic message. */
@@ -397,6 +405,12 @@ struct diagnostic_context
/* Include files that diagnostic_report_current_module has already listed the
include path for. */
hash_set<location_t, false, location_hash> *includes_seen;
+
+ /* A bundle of hooks for providing data to the context about its client
+ e.g. version information, plugins, etc.
+ Used by SARIF output to give metadata about the client that's
+ producing diagnostics. */
+ diagnostic_client_data_hooks *m_client_data_hooks;
};
static inline void
@@ -585,6 +599,9 @@ extern void diagnostic_output_format_init (diagnostic_context *,
extern void diagnostic_output_format_init_json_stderr (diagnostic_context *context);
extern void diagnostic_output_format_init_json_file (diagnostic_context *context,
const char *base_file_name);
+extern void diagnostic_output_format_init_sarif_stderr (diagnostic_context *context);
+extern void diagnostic_output_format_init_sarif_file (diagnostic_context *context,
+ const char *base_file_name);
/* Compute the number of digits in the decimal representation of an integer. */
extern int num_digits (int);
@@ -594,4 +611,6 @@ extern json::value *json_from_expanded_location (diagnostic_context *context,
extern bool warning_enabled_at (location_t, int);
+extern char *get_cwe_url (int cwe);
+
#endif /* ! GCC_DIAGNOSTIC_H */
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index d85b66f..8cd5bdd 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -301,7 +301,7 @@ Objective-C and Objective-C++ Dialects}.
-fdiagnostics-show-location=@r{[}once@r{|}every-line@r{]} @gol
-fdiagnostics-color=@r{[}auto@r{|}never@r{|}always@r{]} @gol
-fdiagnostics-urls=@r{[}auto@r{|}never@r{|}always@r{]} @gol
--fdiagnostics-format=@r{[}text@r{|}json@r{|}json-stderr@r{|}json-file@r{]} @gol
+-fdiagnostics-format=@r{[}text@r{|}sarif-stderr@r{|}sarif-file@r{|}json@r{|}json-stderr@r{|}json-file@r{]} @gol
-fno-diagnostics-show-option -fno-diagnostics-show-caret @gol
-fno-diagnostics-show-labels -fno-diagnostics-show-line-numbers @gol
-fno-diagnostics-show-cwe @gol
@@ -5305,11 +5305,15 @@ Unicode characters. For the example above, the following will be printed:
@item -fdiagnostics-format=@var{FORMAT}
@opindex fdiagnostics-format
Select a different format for printing diagnostics.
-@var{FORMAT} is @samp{text}, @samp{json}, @samp{json-stderr},
-or @samp{json-file}.
+@var{FORMAT} is @samp{text}, @samp{sarif-stderr}, @samp{sarif-file},
+@samp{json}, @samp{json-stderr}, or @samp{json-file}.
The default is @samp{text}.
+The @samp{sarif-stderr} and @samp{sarif-file} formats both emit
+diagnostics in SARIF Version 2.1.0 format, either to stderr, or to a file
+named @file{@var{source}.sarif}, respectively.
+
The @samp{json} format is a synonym for @samp{json-stderr}.
The @samp{json-stderr} and @samp{json-file} formats are identical, apart from
where the JSON is emitted to - with the former, the JSON is emitted to stderr,
diff --git a/gcc/doc/sourcebuild.texi b/gcc/doc/sourcebuild.texi
index 286b1eb..606ab85 100644
--- a/gcc/doc/sourcebuild.texi
+++ b/gcc/doc/sourcebuild.texi
@@ -3152,6 +3152,12 @@ Passes if @var{regexp} matches in Fortran module @var{module}.
@item dg-check-dot @var{filename}
Passes if @var{filename} is a valid @file{.dot} file (by running
@code{dot -Tpng} on it, and verifying the exit code is 0).
+@item scan-sarif-file @var{regexp} [@{ target/xfail @var{selector} @}]
+Passes if @var{regexp} matches text in the file generated by
+@option{-fdiagnostics-format=sarif-file}.
+@item scan-sarif-file-not @var{regexp} [@{ target/xfail @var{selector} @}]
+Passes if @var{regexp} does not match text in the file generated by
+@option{-fdiagnostics-format=sarif-file}.
@end table
@subsubsection Scan the assembly output
diff --git a/gcc/fortran/f95-lang.cc b/gcc/fortran/f95-lang.cc
index e83fef3..319cf8f 100644
--- a/gcc/fortran/f95-lang.cc
+++ b/gcc/fortran/f95-lang.cc
@@ -100,6 +100,15 @@ static const struct attribute_spec gfc_attribute_table[] =
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
+/* Get a value for the SARIF v2.1.0 "artifact.sourceLanguage" property,
+ based on the list in SARIF v2.1.0 Appendix J. */
+
+static const char *
+gfc_get_sarif_source_language (const char *)
+{
+ return "fortran";
+}
+
#undef LANG_HOOKS_NAME
#undef LANG_HOOKS_INIT
#undef LANG_HOOKS_FINISH
@@ -138,6 +147,7 @@ static const struct attribute_spec gfc_attribute_table[] =
#undef LANG_HOOKS_BUILTIN_FUNCTION
#undef LANG_HOOKS_GET_ARRAY_DESCR_INFO
#undef LANG_HOOKS_ATTRIBUTE_TABLE
+#undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
/* Define lang hooks. */
#define LANG_HOOKS_NAME "GNU Fortran"
@@ -177,6 +187,7 @@ static const struct attribute_spec gfc_attribute_table[] =
#define LANG_HOOKS_BUILTIN_FUNCTION gfc_builtin_function
#define LANG_HOOKS_GET_ARRAY_DESCR_INFO gfc_get_array_descr_info
#define LANG_HOOKS_ATTRIBUTE_TABLE gfc_attribute_table
+#define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE gfc_get_sarif_source_language
struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
diff --git a/gcc/go/go-lang.cc b/gcc/go/go-lang.cc
index c8365d2..84cd623 100644
--- a/gcc/go/go-lang.cc
+++ b/gcc/go/go-lang.cc
@@ -545,6 +545,15 @@ go_langhook_eh_personality (void)
return personality_decl;
}
+/* Get a value for the SARIF v2.1.0 "artifact.sourceLanguage" property,
+ based on the list in SARIF v2.1.0 Appendix J. */
+
+static const char *
+go_get_sarif_source_language (const char *)
+{
+ return "go";
+}
+
/* Functions called directly by the generic backend. */
tree
@@ -615,6 +624,7 @@ go_localize_identifier (const char *ident)
#undef LANG_HOOKS_GETDECLS
#undef LANG_HOOKS_GIMPLIFY_EXPR
#undef LANG_HOOKS_EH_PERSONALITY
+#undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
#define LANG_HOOKS_NAME "GNU Go"
#define LANG_HOOKS_INIT go_langhook_init
@@ -631,6 +641,7 @@ go_localize_identifier (const char *ident)
#define LANG_HOOKS_GETDECLS go_langhook_getdecls
#define LANG_HOOKS_GIMPLIFY_EXPR go_langhook_gimplify_expr
#define LANG_HOOKS_EH_PERSONALITY go_langhook_eh_personality
+#define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE go_get_sarif_source_language
struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index 95d8dec..4e17915 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -98,6 +98,7 @@ extern const char *lhd_get_substring_location (const substring_loc &,
extern int lhd_decl_dwarf_attribute (const_tree, int);
extern int lhd_type_dwarf_attribute (const_tree, int);
extern void lhd_finalize_early_debug (void);
+extern const char *lhd_get_sarif_source_language (const char *);
#define LANG_HOOKS_NAME "GNU unknown"
#define LANG_HOOKS_IDENTIFIER_SIZE sizeof (struct lang_identifier)
@@ -150,6 +151,7 @@ extern void lhd_finalize_early_debug (void);
#define LANG_HOOKS_RUN_LANG_SELFTESTS lhd_do_nothing
#define LANG_HOOKS_GET_SUBSTRING_LOCATION lhd_get_substring_location
#define LANG_HOOKS_FINALIZE_EARLY_DEBUG lhd_finalize_early_debug
+#define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE lhd_get_sarif_source_language
/* Attribute hooks. */
#define LANG_HOOKS_ATTRIBUTE_TABLE NULL
@@ -394,7 +396,8 @@ extern void lhd_end_section (void);
LANG_HOOKS_EMITS_BEGIN_STMT, \
LANG_HOOKS_RUN_LANG_SELFTESTS, \
LANG_HOOKS_GET_SUBSTRING_LOCATION, \
- LANG_HOOKS_FINALIZE_EARLY_DEBUG \
+ LANG_HOOKS_FINALIZE_EARLY_DEBUG, \
+ LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE \
}
#endif /* GCC_LANG_HOOKS_DEF_H */
diff --git a/gcc/langhooks.cc b/gcc/langhooks.cc
index 97e5139..a933407 100644
--- a/gcc/langhooks.cc
+++ b/gcc/langhooks.cc
@@ -925,6 +925,14 @@ lhd_finalize_early_debug (void)
(*debug_hooks->early_global_decl) (cnode->decl);
}
+/* Default implementation of LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE. */
+
+const char *
+lhd_get_sarif_source_language (const char *)
+{
+ return NULL;
+}
+
/* Returns true if the current lang_hooks represents the GNU C frontend. */
bool
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index 7502555..97aa9e0 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -640,6 +640,12 @@ struct lang_hooks
/* Invoked before the early_finish debug hook is invoked. */
void (*finalize_early_debug) (void);
+ /* Get a value for the SARIF v2.1.0 "artifact.sourceLanguage" property
+ for FILENAME, or return NULL.
+ See SARIF v2.1.0 Appendix J for suggested values for common programming
+ languages. */
+ const char *(*get_sarif_source_language) (const char *filename);
+
/* Whenever you add entries here, make sure you adjust langhooks-def.h
and langhooks.cc accordingly. */
};
diff --git a/gcc/logical-location.h b/gcc/logical-location.h
new file mode 100644
index 0000000..2e7b8e3
--- /dev/null
+++ b/gcc/logical-location.h
@@ -0,0 +1,72 @@
+/* Logical location support, without knowledge of "tree".
+ Copyright (C) 2022 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_LOGICAL_LOCATION_H
+#define GCC_LOGICAL_LOCATION_H
+
+/* An enum for discriminating between different kinds of logical location
+ for a diagnostic.
+
+ Roughly corresponds to logicalLocation's "kind" property in SARIF v2.1.0
+ (section 3.33.7). */
+
+enum logical_location_kind
+{
+ LOGICAL_LOCATION_KIND_UNKNOWN,
+
+ LOGICAL_LOCATION_KIND_FUNCTION,
+ LOGICAL_LOCATION_KIND_MEMBER,
+ LOGICAL_LOCATION_KIND_MODULE,
+ LOGICAL_LOCATION_KIND_NAMESPACE,
+ LOGICAL_LOCATION_KIND_TYPE,
+ LOGICAL_LOCATION_KIND_RETURN_TYPE,
+ LOGICAL_LOCATION_KIND_PARAMETER,
+ LOGICAL_LOCATION_KIND_VARIABLE
+};
+
+/* Abstract base class for passing around logical locations in the
+ diagnostics subsystem, such as:
+ - "within function 'foo'", or
+ - "within method 'bar'",
+ but *without* requiring knowledge of trees
+ (see tree-logical-location.h for subclasses relating to trees). */
+
+class logical_location
+{
+public:
+ virtual ~logical_location () {}
+
+ /* Get a string (or NULL) suitable for use by the SARIF logicalLocation
+ "name" property (SARIF v2.1.0 section 3.33.4). */
+ virtual const char *get_short_name () const = 0;
+
+ /* Get a string (or NULL) suitable for use by the SARIF logicalLocation
+ "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
+ virtual const char *get_name_with_scope () const = 0;
+
+ /* Get a string (or NULL) suitable for use by the SARIF logicalLocation
+ "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
+ virtual const char *get_internal_name () const = 0;
+
+ /* Get what kind of SARIF logicalLocation this is (if any). */
+ virtual enum logical_location_kind get_kind () const = 0;
+};
+
+#endif /* GCC_LOGICAL_LOCATION_H. */
diff --git a/gcc/objc/objc-act.h b/gcc/objc/objc-act.h
index 7d0c6d5..4f9c3a2 100644
--- a/gcc/objc/objc-act.h
+++ b/gcc/objc/objc-act.h
@@ -27,6 +27,7 @@ bool objc_init (void);
const char *objc_printable_name (tree, int);
int objc_gimplify_expr (tree *, gimple_seq *, gimple_seq *);
void objc_common_init_ts (void);
+const char *objc_get_sarif_source_language (const char *);
/* NB: The remaining public functions are prototyped in c-common.h, for the
benefit of stub-objc.cc and objc-act.cc. */
diff --git a/gcc/objc/objc-lang.cc b/gcc/objc/objc-lang.cc
index ef664f5..559de4b 100644
--- a/gcc/objc/objc-lang.cc
+++ b/gcc/objc/objc-lang.cc
@@ -46,10 +46,18 @@ enum c_language_kind c_language = clk_objc;
#define LANG_HOOKS_INIT_TS objc_common_init_ts
#undef LANG_HOOKS_TREE_SIZE
#define LANG_HOOKS_TREE_SIZE objc_common_tree_size
+#undef LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE
+#define LANG_HOOKS_GET_SARIF_SOURCE_LANGUAGE objc_get_sarif_source_language
/* Each front end provides its own lang hook initializer. */
struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
+const char *
+objc_get_sarif_source_language (const char *)
+{
+ return "objectivec";
+}
+
/* Lang hook routines common to C and ObjC appear in c-objc-common.cc;
there should be very few (if any) routines below. */
diff --git a/gcc/plugin.cc b/gcc/plugin.cc
index 17b33e4..6c42e05 100644
--- a/gcc/plugin.cc
+++ b/gcc/plugin.cc
@@ -815,6 +815,44 @@ finalize_plugins (void)
plugin_name_args_tab = NULL;
}
+/* Implementation detail of for_each_plugin. */
+
+struct for_each_plugin_closure
+{
+ void (*cb) (const plugin_name_args *,
+ void *user_data);
+ void *user_data;
+};
+
+/* Implementation detail of for_each_plugin: callback for htab_traverse_noresize
+ that calls the user-provided callback. */
+
+static int
+for_each_plugin_cb (void **slot, void *info)
+{
+ struct plugin_name_args *plugin = (struct plugin_name_args *) *slot;
+ for_each_plugin_closure *c = (for_each_plugin_closure *)info;
+ c->cb (plugin, c->user_data);
+ return 1;
+}
+
+/* Call CB with USER_DATA on each plugin. */
+
+void
+for_each_plugin (void (*cb) (const plugin_name_args *,
+ void *user_data),
+ void *user_data)
+{
+ if (!plugin_name_args_tab)
+ return;
+
+ for_each_plugin_closure c;
+ c.cb = cb;
+ c.user_data = user_data;
+
+ htab_traverse_noresize (plugin_name_args_tab, for_each_plugin_cb, &c);
+}
+
/* Used to pass options to htab_traverse callbacks. */
struct print_options
diff --git a/gcc/plugin.h b/gcc/plugin.h
index ff999c4..e7e8b51 100644
--- a/gcc/plugin.h
+++ b/gcc/plugin.h
@@ -170,6 +170,9 @@ extern void warn_if_plugins (void);
extern void print_plugins_versions (FILE *file, const char *indent);
extern void print_plugins_help (FILE *file, const char *indent);
extern void finalize_plugins (void);
+extern void for_each_plugin (void (*cb) (const plugin_name_args *,
+ void *user_data),
+ void *user_data);
extern bool flag_plugin_added;
diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-1.c b/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-1.c
new file mode 100644
index 0000000..4d19ae1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-1.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-format=sarif-file" } */
+
+#warning message
+
+/* Verify that some JSON was written to a file with the expected name. */
+
+/* We expect various properties.
+ The indentation here reflects the expected hierarchy, though these tests
+ don't check for that, merely the string fragments we expect.
+ { dg-final { scan-sarif-file "\"version\": \"2.1.0\"" } }
+ { dg-final { scan-sarif-file "\"runs\": \\\[" } }
+ { dg-final { scan-sarif-file "\"artifacts\": \\\[" } }
+ { dg-final { scan-sarif-file "\"location\": " } }
+ { dg-final { scan-sarif-file "\"uri\": " } }
+
+ { dg-final { scan-sarif-file "\"sourceLanguage\": \"c\"" { target c } } }
+ { dg-final { scan-sarif-file "\"sourceLanguage\": \"cplusplus\"" { target c++ } } }
+
+ { dg-final { scan-sarif-file "\"contents\": " } }
+ { dg-final { scan-sarif-file "\"text\": " } }
+ { dg-final { scan-sarif-file "\"tool\": " } }
+ { dg-final { scan-sarif-file "\"driver\": " } }
+ { dg-final { scan-sarif-file "\"name\": \"GNU C" } }
+ { dg-final { scan-sarif-file "\"fullName\": \"GNU C" } }
+ { dg-final { scan-sarif-file "\"informationUri\": \"" } }
+ { dg-final { scan-sarif-file "\"results\": \\\[" } }
+ { dg-final { scan-sarif-file "\"level\": \"warning\"" } }
+ { dg-final { scan-sarif-file "\"ruleId\": \"-Wcpp\"" } }
+ { dg-final { scan-sarif-file "\"locations\": \\\[" } }
+ { dg-final { scan-sarif-file "\"physicalLocation\": " } }
+ { dg-final { scan-sarif-file "\"contextRegion\": " } }
+ { dg-final { scan-sarif-file "\"artifactLocation\": " } }
+ { dg-final { scan-sarif-file "\"region\": " } }
+ { dg-final { scan-sarif-file "\"startLine\": 4" } }
+ { dg-final { scan-sarif-file "\"startColumn\": 2" } }
+ { dg-final { scan-sarif-file "\"endColumn\": 9" } }
+
+ We don't expect logical locations for a top-level warning:
+ { dg-final { scan-sarif-file-not "\"logicalLocations\": " } }
+
+ { dg-final { scan-sarif-file "\"message\": " } }
+ { dg-final { scan-sarif-file "\"text\": \"#warning message" } } */
diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-2.c b/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-2.c
new file mode 100644
index 0000000..8f5814d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-2.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-format=sarif-file -Wmisleading-indentation" } */
+
+int test (void)
+{
+ if (1)
+ return 3;
+ return 4;
+ return 5;
+}
+
+/*
+ { dg-final { scan-sarif-file "\"level\": \"warning\"" } }
+ { dg-final { scan-sarif-file "\"ruleId\": \"-Wmisleading-indentation\"" } }
+ { dg-final { scan-sarif-file "\"text\": \" if " } }
+
+ { dg-final { scan-sarif-file "\"locations\": \\\[" } }
+
+ We expect a logical location for the error (within fn "test"):
+ { dg-final { scan-sarif-file "\"logicalLocations\": \\\[" } }
+ { dg-final { scan-sarif-file "\"kind\": \"function\"" } }
+ { dg-final { scan-sarif-file "\"name\": \"test\"" } }
+ { dg-final { scan-sarif-file "\"fullyQualifiedName\": \"test\"" } }
+ { dg-final { scan-sarif-file "\"decoratedName\": \"" } }
+
+ We expect the "note" to become a "relatedLocations" entry:
+ { dg-final { scan-sarif-file "\"relatedLocations\": \\\[" } }
+ { dg-final { scan-sarif-file "\"text\": \" return 4;" } }
+*/
diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-3.c b/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-3.c
new file mode 100644
index 0000000..3856782
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-3.c
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-format=sarif-file" } */
+/* { dg-excess-errors "The error is sent to the SARIF file, rather than stderr" } */
+
+struct s { int color; };
+
+int test (struct s *ptr)
+{
+ return ptr->colour;
+}
+
+/*
+ { dg-final { scan-sarif-file "\"level\": \"error\"" } }
+
+ We expect a logical location for the error (within fn "test"):
+ { dg-final { scan-sarif-file "\"locations\": \\\[" } }
+ { dg-final { scan-sarif-file "\"logicalLocations\": \\\[" } }
+ { dg-final { scan-sarif-file "\"kind\": \"function\"" } }
+ { dg-final { scan-sarif-file "\"name\": \"test\"" } }
+ { dg-final { scan-sarif-file "\"fullyQualifiedName\": \"test\"" } }
+ { dg-final { scan-sarif-file "\"decoratedName\": \"" } }
+
+ We expect a "fixes" array for the fix-it hint (SARIF v2.1.0 section 3.27.30):
+ { dg-final { scan-sarif-file "\"fixes\": \\\[" } }
+ { dg-final { scan-sarif-file "\"artifactChanges\": \\\[" } }
+ { dg-final { scan-sarif-file "\"replacements\": \\\[" } }
+ { dg-final { scan-sarif-file "\"insertedContent\": " } }
+ { dg-final { scan-sarif-file "\"text\": \"color\"" } }
+ { dg-final { scan-sarif-file "\"deletedRegion\": " } }
+*/
diff --git a/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-4.c b/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-4.c
new file mode 100644
index 0000000..2d22f54
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/diagnostic-format-sarif-file-4.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-format=sarif-file" } */
+/* { dg-excess-errors "The error is sent to the SARIF file, rather than stderr" } */
+
+int test (void)
+{
+ int 文字化け = *42;
+}
+
+/*
+ { dg-final { scan-sarif-file "\"level\": \"error\"" } }
+
+ We expect the region expressed in display columns:
+ { dg-final { scan-sarif-file "\"startLine\": 7" } }
+ { dg-final { scan-sarif-file "\"startColumn\": 18" } }
+ { dg-final { scan-sarif-file "\"endColumn\": 21" } }
+
+ { dg-final { scan-sarif-file "\"text\": \" int \\u6587\\u5b57\\u5316\\u3051 = " } }
+*/
diff --git a/gcc/testsuite/gcc.dg/analyzer/file-meaning-1.c b/gcc/testsuite/gcc.dg/analyzer/file-meaning-1.c
new file mode 100644
index 0000000..66b72a7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/file-meaning-1.c
@@ -0,0 +1,15 @@
+/* { dg-additional-options "-fanalyzer-verbose-state-changes" } */
+
+typedef struct FILE FILE;
+FILE* fopen (const char*, const char*);
+int fclose (FILE*);
+
+void test_1 (const char *path)
+{
+ FILE *f = fopen (path, "r"); /* { dg-message "meaning: \\{verb: 'acquire', noun: 'resource'\\}" } */
+ if (!f)
+ return;
+
+ fclose (f); /* { dg-message "meaning: \\{verb: 'release', noun: 'resource'\\}" } */
+ fclose (f); /* { dg-warning "double 'fclose' of FILE 'f'" "warning" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-meaning-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-meaning-1.c
new file mode 100644
index 0000000..4964e25
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-meaning-1.c
@@ -0,0 +1,10 @@
+/* { dg-additional-options "-fanalyzer-verbose-state-changes" } */
+
+#include <stdlib.h>
+
+void test_1 (void)
+{
+ void *ptr = malloc (1024); /* { dg-message "meaning: \\{verb: 'acquire', noun: 'memory'\\}" } */
+ free (ptr); /* { dg-message "meaning: \\{verb: 'release', noun: 'memory'\\}" } */
+ free (ptr); /* { dg-warning "double-'free' of 'ptr'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-sarif-1.c b/gcc/testsuite/gcc.dg/analyzer/malloc-sarif-1.c
new file mode 100644
index 0000000..3d141b5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-sarif-1.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdiagnostics-format=sarif-file" } */
+
+#include <stdlib.h>
+
+void test_1 (void)
+{
+ void *ptr = malloc (1024);
+ free (ptr);
+ free (ptr);
+}
+
+/* Verify SARIF output.
+
+ The threadFlowLocation objects should have "kinds" properties
+ reflecting the meanings of the events:
+ { dg-final { scan-sarif-file "\"kinds\": \\\[\"acquire\", \"memory\"\\\]" } }
+ { dg-final { scan-sarif-file "\"kinds\": \\\[\"release\", \"memory\"\\\]" } }
+ { dg-final { scan-sarif-file "\"kinds\": \\\[\"danger\"\\\]" } }
+*/
diff --git a/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c b/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c
index b5ae128..2a8bf11 100644
--- a/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c
+++ b/gcc/testsuite/gcc.dg/plugin/analyzer_gil_plugin.c
@@ -109,6 +109,21 @@ public:
return label_text ();
}
+ diagnostic_event::meaning
+ get_meaning_for_state_change (const evdesc::state_change &change)
+ const final override
+ {
+ if (change.is_global_p ())
+ {
+ if (change.m_new_state == m_sm.m_released_gil)
+ return diagnostic_event::meaning (diagnostic_event::VERB_release,
+ diagnostic_event::NOUN_lock);
+ else if (change.m_new_state == m_sm.get_start_state ())
+ return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
+ diagnostic_event::NOUN_lock);
+ }
+ return diagnostic_event::meaning ();
+ }
protected:
gil_diagnostic (const gil_state_machine &sm) : m_sm (sm)
{
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-5.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-5.c
new file mode 100644
index 0000000..bd09391
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-5.c
@@ -0,0 +1,56 @@
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-format=sarif-file" } */
+/* { dg-excess-errors "The error is sent to the SARIF file, rather than stderr" } */
+
+#include <stddef.h>
+#include <stdlib.h>
+
+/* Minimal reimplementation of cpython API. */
+typedef struct PyObject {} PyObject;
+extern int PyArg_ParseTuple (PyObject *args, const char *fmt, ...);
+extern PyObject *PyList_New (int);
+extern PyObject *PyLong_FromLong(long);
+extern void PyList_Append(PyObject *list, PyObject *item);
+
+PyObject *
+make_a_list_of_random_ints_badly(PyObject *self,
+ PyObject *args)
+{
+ PyObject *list, *item;
+ long count, i;
+
+ if (!PyArg_ParseTuple(args, "i", &count)) {
+ return NULL;
+ }
+
+ list = PyList_New(0);
+
+ for (i = 0; i < count; i++) {
+ item = PyLong_FromLong(random());
+ PyList_Append(list, item);
+ }
+
+ return list;
+}
+
+/*
+ { dg-final { scan-sarif-file "\"tool\": " } }
+
+ We expect info about the plugin:
+ { dg-final { scan-sarif-file "\"extensions\": \\\[" } }
+ { dg-final { scan-sarif-file "\"name\": \"diagnostic_plugin_test_paths\"" } }
+ { dg-final { scan-sarif-file "\"fullName\": \"" } }
+
+ { dg-final { scan-sarif-file "\"results\": \\\[" } }
+ { dg-final { scan-sarif-file "\"level\": \"error\"" } }
+ { dg-final { scan-sarif-file "\"text\": \"passing NULL as argument 1 to 'PyList_Append' which requires a non-NULL parameter\"" } }
+
+ We expect a path for the diagnostic:
+ { dg-final { scan-sarif-file "\"codeFlows\": \\\[" } }
+ { dg-final { scan-sarif-file "\"threadFlows\": \\\[" } }
+ { dg-final { scan-sarif-file "\"locations\": \\\[" } }
+ { dg-final { scan-sarif-file "\"text\": \"when 'PyList_New' fails, returning NULL\"" } }
+ { dg-final { scan-sarif-file "\"text\": \"when 'i < count'\"" } }
+ { dg-final { scan-sarif-file "\"text\": \"when calling 'PyList_Append', passing NULL from \\(1\\) as argument 1\"" } }
+
+*/
diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
index 2ade945..63b117d 100644
--- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -102,6 +102,7 @@ set plugin_test_list [list \
diagnostic-test-paths-2.c \
diagnostic-test-paths-3.c \
diagnostic-test-paths-4.c \
+ diagnostic-test-paths-5.c \
diagnostic-path-format-plain.c \
diagnostic-path-format-none.c \
diagnostic-path-format-separate-events.c \
diff --git a/gcc/testsuite/lib/gcc-dg.exp b/gcc/testsuite/lib/gcc-dg.exp
index 8c28997..f58b9e6 100644
--- a/gcc/testsuite/lib/gcc-dg.exp
+++ b/gcc/testsuite/lib/gcc-dg.exp
@@ -25,6 +25,7 @@ load_lib scanltranstree.exp
load_lib scanipa.exp
load_lib scanwpaipa.exp
load_lib scanlang.exp
+load_lib scansarif.exp
load_lib timeout.exp
load_lib timeout-dg.exp
load_lib prune.exp
diff --git a/gcc/testsuite/lib/scansarif.exp b/gcc/testsuite/lib/scansarif.exp
new file mode 100644
index 0000000..8b7e89c
--- /dev/null
+++ b/gcc/testsuite/lib/scansarif.exp
@@ -0,0 +1,42 @@
+# Copyright (C) 2000-2022 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+#
+# This program 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/>.
+
+# Various utilities for scanning SARIF output, used by gcc-dg.exp and
+# g++-dg.exp.
+#
+# This is largely borrowed from scanasm.exp.
+
+# Look for a pattern in the .sarif file produced by the compiler. See
+# dg-scan for details.
+
+proc scan-sarif-file { args } {
+ set testcase [testname-for-summary]
+ # The name might include a list of options; extract the file name.
+ set filename [lindex $testcase 0]
+ set output_file "[file tail $filename].sarif"
+ dg-scan "scan-sarif-file" 1 $testcase $output_file $args
+}
+
+# Check that a pattern is not present in the .sarif file. See dg-scan
+# for details.
+
+proc scan-sarif-file-not { args } {
+ set testcase [testname-for-summary]
+ # The name might include a list of options; extract the file name.
+ set filename [lindex $testcase 0]
+ set output_file "[file tail $filename].sarif"
+ dg-scan "scan-sarif-file-not" 0 $testcase $output_file $args
+}
diff --git a/gcc/tree-diagnostic-client-data-hooks.cc b/gcc/tree-diagnostic-client-data-hooks.cc
new file mode 100644
index 0000000..f8ff271
--- /dev/null
+++ b/gcc/tree-diagnostic-client-data-hooks.cc
@@ -0,0 +1,150 @@
+/* Implementation of diagnostic_client_data_hooks for the compilers
+ (e.g. with knowledge of "tree" and lang_hooks).
+ Copyright (C) 2022 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/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "version.h"
+#include "tree.h"
+#include "diagnostic.h"
+#include "tree-logical-location.h"
+#include "diagnostic-client-data-hooks.h"
+#include "langhooks.h"
+#include "plugin.h"
+
+/* Concrete class for supplying a diagnostic_context with information
+ about a specific plugin within the client, when the client is the
+ compiler (i.e. a GCC plugin). */
+
+class compiler_diagnostic_client_plugin_info
+ : public diagnostic_client_plugin_info
+{
+public:
+ compiler_diagnostic_client_plugin_info (const plugin_name_args *args)
+ : m_args (args)
+ {
+ }
+
+ const char *get_short_name () const final override
+ {
+ return m_args->base_name;
+ }
+
+ const char *get_full_name () const final override
+ {
+ return m_args->full_name;
+ }
+
+ const char *get_version () const final override
+ {
+ return m_args->version;
+ }
+
+private:
+ const plugin_name_args *m_args;
+};
+
+/* Concrete subclass of client_version_info for use by compilers proper,
+ (i.e. using lang_hooks, and with knowledge of GCC plugins). */
+
+class compiler_version_info : public client_version_info
+{
+public:
+ const char *get_tool_name () const final override
+ {
+ return lang_hooks.name;
+ }
+
+ /* Compare with toplev.cc: print_version.
+ TARGET_NAME is passed in by the Makefile. */
+ char *
+ maybe_make_full_name () const final override
+ {
+ return xasprintf ("%s %sversion %s (%s)",
+ get_tool_name (), pkgversion_string, version_string,
+ TARGET_NAME);
+ }
+
+ const char *get_version_string () const final override
+ {
+ return version_string;
+ }
+
+ char *maybe_make_version_url () const final override
+ {
+ return xasprintf ("https://gcc.gnu.org/gcc-%i/", GCC_major_version);
+ }
+
+ void for_each_plugin (plugin_visitor &visitor) const final override
+ {
+ ::for_each_plugin (on_plugin_cb, &visitor);
+ }
+
+private:
+ static void
+ on_plugin_cb (const plugin_name_args *args,
+ void *user_data)
+ {
+ compiler_diagnostic_client_plugin_info cpi (args);
+ client_version_info::plugin_visitor *visitor
+ = (client_version_info::plugin_visitor *)user_data;
+ visitor->on_plugin (cpi);
+ }
+};
+
+/* Subclass of diagnostic_client_data_hooks for use by compilers proper
+ i.e. with knowledge of "tree", access to langhooks, etc. */
+
+class compiler_data_hooks : public diagnostic_client_data_hooks
+{
+public:
+ const client_version_info *get_any_version_info () const final override
+ {
+ return &m_version_info;
+ }
+
+ const logical_location *get_current_logical_location () const final override
+ {
+ if (current_function_decl)
+ return &m_current_fndecl_logical_loc;
+ else
+ return NULL;
+ }
+
+ const char *
+ maybe_get_sarif_source_language (const char *filename) const final override
+ {
+ return lang_hooks.get_sarif_source_language (filename);
+ }
+
+private:
+ compiler_version_info m_version_info;
+ current_fndecl_logical_location m_current_fndecl_logical_loc;
+};
+
+/* Create a compiler_data_hooks (so that the class can be local
+ to this file). */
+
+diagnostic_client_data_hooks *
+make_compiler_data_hooks ()
+{
+ return new compiler_data_hooks ();
+}
diff --git a/gcc/tree-diagnostic.cc b/gcc/tree-diagnostic.cc
index 40a4c5f..0d79fe3 100644
--- a/gcc/tree-diagnostic.cc
+++ b/gcc/tree-diagnostic.cc
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-pretty-print.h"
#include "gimple-pretty-print.h"
#include "tree-diagnostic.h"
+#include "diagnostic-client-data-hooks.h"
#include "langhooks.h"
#include "intl.h"
@@ -373,4 +374,5 @@ tree_diagnostics_defaults (diagnostic_context *context)
context->print_path = default_tree_diagnostic_path_printer;
context->make_json_for_path = default_tree_make_json_for_path;
context->set_locations_cb = set_inlining_locations;
+ context->m_client_data_hooks = make_compiler_data_hooks ();
}
diff --git a/gcc/tree-logical-location.cc b/gcc/tree-logical-location.cc
new file mode 100644
index 0000000..79d8add
--- /dev/null
+++ b/gcc/tree-logical-location.cc
@@ -0,0 +1,148 @@
+/* Subclasses of logical_location with knowledge of "tree".
+ Copyright (C) 2022 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/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "pretty-print.h"
+#include "tree-logical-location.h"
+#include "langhooks.h"
+
+/* class compiler_logical_location : public logical_location. */
+
+/* Get a string for DECL suitable for use by the SARIF logicalLocation
+ "name" property (SARIF v2.1.0 section 3.33.4). */
+
+const char *
+compiler_logical_location::get_short_name_for_tree (tree decl)
+{
+ gcc_assert (decl);
+ return identifier_to_locale (lang_hooks.decl_printable_name (decl, 0));
+}
+
+/* Get a string for DECL suitable for use by the SARIF logicalLocation
+ "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
+
+const char *
+compiler_logical_location::get_name_with_scope_for_tree (tree decl)
+{
+ gcc_assert (decl);
+ return identifier_to_locale (lang_hooks.decl_printable_name (decl, 1));
+}
+
+/* Get a string for DECL suitable for use by the SARIF logicalLocation
+ "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
+
+const char *
+compiler_logical_location::get_internal_name_for_tree (tree decl)
+{
+ gcc_assert (decl);
+ if (HAS_DECL_ASSEMBLER_NAME_P (decl))
+ if (tree id = DECL_ASSEMBLER_NAME (decl))
+ return IDENTIFIER_POINTER (id);
+ return NULL;
+}
+
+/* Get what kind of SARIF logicalLocation DECL is (if any). */
+
+enum logical_location_kind
+compiler_logical_location::get_kind_for_tree (tree decl)
+{
+ if (!decl)
+ return LOGICAL_LOCATION_KIND_UNKNOWN;
+
+ switch (TREE_CODE (decl))
+ {
+ default:
+ return LOGICAL_LOCATION_KIND_UNKNOWN;
+ case FUNCTION_DECL:
+ return LOGICAL_LOCATION_KIND_FUNCTION;
+ case PARM_DECL:
+ return LOGICAL_LOCATION_KIND_PARAMETER;
+ case VAR_DECL:
+ return LOGICAL_LOCATION_KIND_VARIABLE;
+ }
+}
+
+/* class tree_logical_location : public compiler_logical_location. */
+
+/* Implementation of the logical_location vfuncs, using m_decl. */
+
+const char *
+tree_logical_location::get_short_name () const
+{
+ gcc_assert (m_decl);
+ return get_short_name_for_tree (m_decl);
+}
+
+const char *
+tree_logical_location::get_name_with_scope () const
+{
+ gcc_assert (m_decl);
+ return get_name_with_scope_for_tree (m_decl);
+}
+
+const char *
+tree_logical_location::get_internal_name () const
+{
+ gcc_assert (m_decl);
+ return get_internal_name_for_tree (m_decl);
+}
+
+enum logical_location_kind
+tree_logical_location::get_kind () const
+{
+ gcc_assert (m_decl);
+ return get_kind_for_tree (m_decl);
+}
+
+/* class current_fndecl_logical_location : public compiler_logical_location. */
+
+/* Implementation of the logical_location vfuncs, using
+ current_function_decl. */
+
+const char *
+current_fndecl_logical_location::get_short_name () const
+{
+ gcc_assert (current_function_decl);
+ return get_short_name_for_tree (current_function_decl);
+}
+
+const char *
+current_fndecl_logical_location::get_name_with_scope () const
+{
+ gcc_assert (current_function_decl);
+ return get_name_with_scope_for_tree (current_function_decl);
+}
+
+const char *
+current_fndecl_logical_location::get_internal_name () const
+{
+ gcc_assert (current_function_decl);
+ return get_internal_name_for_tree (current_function_decl);
+}
+
+enum logical_location_kind
+current_fndecl_logical_location::get_kind () const
+{
+ gcc_assert (current_function_decl);
+ return get_kind_for_tree (current_function_decl);
+}
diff --git a/gcc/tree-logical-location.h b/gcc/tree-logical-location.h
new file mode 100644
index 0000000..3086cac
--- /dev/null
+++ b/gcc/tree-logical-location.h
@@ -0,0 +1,67 @@
+/* Subclasses of logical_location with knowledge of "tree".
+ Copyright (C) 2022 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_TREE_LOGICAL_LOCATION_H
+#define GCC_TREE_LOGICAL_LOCATION_H
+
+#include "logical-location.h"
+
+/* Abstract subclass of logical_location, with knowledge of "tree", but
+ for no specific tree. */
+
+class compiler_logical_location : public logical_location
+{
+ protected:
+ static const char *get_short_name_for_tree (tree);
+ static const char *get_name_with_scope_for_tree (tree);
+ static const char *get_internal_name_for_tree (tree);
+ static enum logical_location_kind get_kind_for_tree (tree);
+};
+
+/* Concrete subclass of logical_location, with reference to a specific
+ tree. */
+
+class tree_logical_location : public compiler_logical_location
+{
+public:
+ tree_logical_location (tree decl) : m_decl (decl) {}
+
+ const char *get_short_name () const final override;
+ const char *get_name_with_scope () const final override;
+ const char *get_internal_name () const final override;
+ enum logical_location_kind get_kind () const final override;
+
+private:
+ tree m_decl;
+};
+
+/* Concrete subclass of logical_location, with reference to
+ current_function_decl. */
+
+class current_fndecl_logical_location : public compiler_logical_location
+{
+public:
+ const char *get_short_name () const final override;
+ const char *get_name_with_scope () const final override;
+ const char *get_internal_name () const final override;
+ enum logical_location_kind get_kind () const final override;
+};
+
+#endif /* GCC_TREE_LOGICAL_LOCATION_H. */