@@ -1280,6 +1280,7 @@ OBJS = \
expr.o \
fibonacci_heap.o \
final.o \
+ firehose.o \
fixed-value.o \
fold-const.o \
fold-const-call.o \
new file mode 100644
@@ -0,0 +1,709 @@
+/* Serialization format for checker results.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "firehose.h"
+#include "selftest.h"
+#include "selftest-input.h"
+
+namespace firehose {
+
+/* Attempt to parse JV as a json object containing "line" and "column"
+ attributes (a serialization of a firehose.model.Point python object).
+
+ If successful, write a location_t to OUT_VALUE, using GIVENPATH as the
+ filename, and return true.
+ Otherwise, write an error message to OUT_ERR (which must be freed by
+ the caller) and return false. */
+
+static bool
+get_location_from_point (const char *givenpath, const json::value *jv,
+ location_t &out_value, char *&out_err)
+{
+ int line;
+ if (!jv->get_int_by_key ("line", line, out_err))
+ return false;
+
+ int column;
+ if (!jv->get_int_by_key ("column", column, out_err))
+ return false;
+
+ out_value
+ = linemap_position_for_file_line_and_column (line_table,
+ givenpath, line, column);
+ return true;
+}
+
+/* As above, but expect JV to be a json object containing a "start"
+ and "end" (a serialization of a firehose.model.Range python object). */
+
+static bool
+get_location_from_range (const char *givenpath, const json::value *jv,
+ location_t &out_value, char *&out_err)
+{
+ const json::value *jv_start;
+ if (!jv->get_value_by_key ("start", jv_start, out_err))
+ return false;
+
+ location_t start;
+ if (!get_location_from_point (givenpath, jv_start,
+ start, out_err))
+ return false;
+
+ const json::value *jv_end;
+ if (!jv->get_value_by_key ("end", jv_end, out_err))
+ return false;
+ location_t end;
+ if (!get_location_from_point (givenpath, jv_end,
+ end, out_err))
+ return false;
+
+ out_value = make_location (start, start, end);
+ return true;
+}
+
+/* Attempt to extract an attribute "location" from JV, where the value
+ ought to be a serialization of a firehose.model.Location python object.
+
+ If successful, write a location_t to OUT_VALUE and return true.
+ Otherwise, write an error message to OUT_ERR (which must be freed by
+ the caller) and return false. */
+
+static bool
+get_location (const json::value *jv, location_t &out_value, char *&out_err)
+{
+ const json::value *location;
+ if (!jv->get_value_by_key ("location", location, out_err))
+ return false;
+
+ const json::value *file;
+ if (!location->get_value_by_key ("file", file, out_err))
+ return false;
+ const char *givenpath;
+ if (!file->get_string_by_key ("givenpath", givenpath, out_err))
+ return false;
+
+ const json::value *point = location->as_object ()->get_if_nonnull ("point");
+ if (point)
+ {
+ if (!get_location_from_point (givenpath, point, out_value, out_err))
+ return false;
+ }
+ else
+ {
+ const json::value *range
+ = location->as_object ()->get_if_nonnull ("range_");
+
+ if (range)
+ {
+ if (!get_location_from_range (givenpath, range, out_value,
+ out_err))
+ return false;
+ }
+ }
+
+ // ignore "function" for now
+ return true;
+}
+
+/* firehose::state's ctor. */
+
+state::state () : m_location (UNKNOWN_LOCATION), m_notes (NULL)
+{
+}
+
+/* firehose::state's dtor. */
+
+state::~state ()
+{
+ free (m_notes);
+}
+
+/* Attempt to allocate a new firehose::state based on JV, which ought to be a
+ serialization of a firehose.model.State python object.
+
+ Return the new state if successful.
+ Otherwise return NULL and write an error message to OUT_ERR
+ (which must be freed by the caller). */
+
+state *
+state::from_json (const json::value *jv, char *&out_err)
+{
+ state *s = new state ();
+
+ /* Extract the state's location to m_location. */
+ if (!get_location (jv, s->m_location, out_err))
+ {
+ delete s;
+ return NULL;
+ }
+
+ /* Get any notes. */
+ json::value *notes = jv->as_object ()->get_if_nonnull ("notes");
+ if (notes)
+ {
+ const char *text;
+ if (!notes->get_string_by_key ("text", text, out_err))
+ {
+ delete s;
+ return NULL;
+ }
+ s->m_notes = xstrdup (text);
+ }
+
+ return s;
+}
+
+/* firehose::trace's dtor. */
+
+trace::~trace ()
+{
+ int i;
+ state *state;
+ FOR_EACH_VEC_ELT (m_states, i, state)
+ delete state;
+}
+
+/* Attempt to allocate a new firehose::trace based on JV, which ought to be a
+ serialization of a firehose.model.State python object.
+
+ Return the new state if successful.
+ Otherwise return NULL and write an error message to OUT_ERR
+ (which must be freed by the caller). */
+
+trace *
+trace::from_json (const json::value *jv, char *&out_err)
+{
+ const json::array *states;
+ if (!jv->get_array_by_key ("states", states, out_err))
+ return NULL;
+
+ trace *t = new trace ();
+ for (unsigned idx = 0; idx < states->get_length (); idx++)
+ {
+ const json::value *item = states->get (idx);
+ if (0)
+ {
+ fprintf (stderr, "got state %i: ", idx);
+ item->dump (stderr);
+ fprintf (stderr, "\n");
+ }
+ firehose::state *state = state::from_json (item, out_err);
+ if (!state)
+ {
+ delete t;
+ return NULL;
+ }
+ t->m_states.safe_push (state);
+ }
+
+ return t;
+}
+
+/* Filter out the states to just those with notes. */
+
+void
+trace::filter ()
+{
+ unsigned idx = 0;
+ while (idx < m_states.length ())
+ {
+ if (m_states[idx]->m_notes == NULL)
+ {
+ delete m_states[idx];
+ m_states.ordered_remove (idx);
+ }
+ else
+ idx++;
+ }
+}
+
+/* Determine if THIS trace is merely a single state that duplicates
+ the information within ISSUE. */
+
+bool
+trace::is_redundant_p (const issue& issue) const
+{
+ if (m_states.length () > 1)
+ return false;
+ if (m_states.length () < 1)
+ return true;
+
+ state *s0 = m_states[0];
+
+ if (s0->m_location != issue.m_location)
+ return false;
+ if (s0->m_notes)
+ if (0 != strcmp (s0->m_notes, issue.m_message))
+ return false;
+
+ /* Single state, with same location, and same message as ISSUE. */
+ return true;
+}
+
+/* firehose::result's ctor. */
+
+result::result ()
+: m_message (NULL), m_location (UNKNOWN_LOCATION)
+{
+}
+
+/* firehose::result's dtor. */
+
+result::~result ()
+{
+ free (m_message);
+}
+
+/* Attempt to allocate a new firehose::result based on JV, which ought to be a
+ serialization of a firehose.model.Result python object.
+
+ Return the new state if successful.
+ Otherwise return NULL and write an error message to OUT_ERR
+ (which must be freed by the caller). */
+
+result *
+result::from_json (const json::value *jv, char *&out_err)
+{
+ const char *type;
+ if (!jv->get_string_by_key ("type", type, out_err))
+ return NULL;
+ result *result = NULL;
+ if (0 == strcmp (type, "Issue"))
+ {
+ result = issue::from_json (jv, out_err);
+ }
+ if (0 == strcmp (type, "Info"))
+ {
+ result = info::from_json (jv, out_err);
+ }
+ if (0 == strcmp (type, "Failure"))
+ {
+ result = failure::from_json (jv, out_err);
+ }
+ if (!result)
+ {
+ out_err = xstrdup ("unrecognized type of result");
+ delete result;
+ return NULL;
+ }
+
+ /* Extract the results's message's text to m_message. */
+ const json::value *message;
+ if (!jv->get_value_by_key ("message", message, out_err))
+ {
+ delete result;
+ return NULL;
+ }
+ const char *message_text;
+ if (!message->get_string_by_key ("text", message_text, out_err))
+ {
+ delete result;
+ return NULL;
+ }
+ result->m_message = xstrdup (message_text);
+
+ /* Extract the result's location to m_location. */
+ if (!get_location (jv, result->m_location, out_err))
+ {
+ delete result;
+ return NULL;
+ }
+
+ return result;
+}
+
+/* firehose::issue's ctor. */
+
+issue::issue () : result (), m_testid (NULL), m_trace (NULL)
+{
+}
+
+/* firehose::issue's dtor. */
+
+issue::~issue ()
+{
+ free (m_testid);
+ delete m_trace;
+}
+
+/* Attempt to allocate a new firehose::issue based on JV, which ought to be a
+ serialization of a firehose.model.Issue python object.
+
+ Return the new state if successful.
+ Otherwise return NULL and write an error message to OUT_ERR
+ (which must be freed by the caller). */
+
+issue *
+issue::from_json (const json::value *jv, char *&out_err)
+{
+ issue *r = new issue ();
+
+ /* FIXME: get any testid. */
+ const char *testid_text = NULL;
+ if (!jv->get_optional_string_by_key ("testid", testid_text, out_err))
+ {
+ delete r;
+ return NULL;
+ }
+ if (testid_text)
+ r->m_testid = xstrdup (testid_text);
+
+ /* Get any trace as m_trace. */
+ const json::value *trace = jv->as_object ()->get_if_nonnull ("trace");
+ if (trace)
+ {
+ r->m_trace = trace::from_json (trace, out_err);
+ if (!r->m_trace)
+ {
+ delete r;
+ return NULL;
+ }
+ }
+
+ return r;
+}
+
+/* firehose::info's ctor. */
+
+info::info () : result (), m_infoid (NULL)
+{
+}
+
+/* firehose::info's dtor. */
+
+info::~info ()
+{
+ free (m_infoid);
+}
+
+/* Attempt to allocate a new firehose::info based on JV, which ought to be a
+ serialization of a firehose.model.Info python object.
+
+ Return the new state if successful.
+ Otherwise return NULL and write an error message to OUT_ERR
+ (which must be freed by the caller). */
+
+info *
+info::from_json (const json::value *jv, char *&out_err)
+{
+ info *r = new info ();
+
+ /* FIXME: get any infoid. */
+ const char *infoid_text = NULL;
+ if (!jv->get_optional_string_by_key ("infoid", infoid_text, out_err))
+ {
+ delete r;
+ return NULL;
+ }
+ if (infoid_text)
+ r->m_infoid = xstrdup (infoid_text);
+
+ return r;
+}
+
+/* firehose::failure's ctor. */
+
+failure::failure () : result (), m_failureid (NULL)
+{
+}
+
+/* firehose::failure's dtor. */
+
+failure::~failure ()
+{
+ free (m_failureid);
+}
+
+/* Attempt to allocate a new firehose::failure based on JV, which ought to be a
+ serialization of a firehose.model.Failure python object.
+
+ Return the new state if successful.
+ Otherwise return NULL and write an error message to OUT_ERR
+ (which must be freed by the caller). */
+
+failure *
+failure::from_json (const json::value *jv, char *&out_err)
+{
+ failure *r = new failure ();
+
+ /* FIXME: get any failureid. */
+ const char *failureid_text = NULL;
+ if (!jv->get_optional_string_by_key ("failureid", failureid_text, out_err))
+ {
+ delete r;
+ return NULL;
+ }
+ if (failureid_text)
+ r->m_failureid = xstrdup (failureid_text);
+
+ return r;
+}
+
+/* firehose::generator's ctor. */
+
+generator::generator ()
+: m_name (NULL), m_version (NULL)
+{
+}
+
+/* firehose::generator's dtor. */
+
+generator::~generator ()
+{
+ free (m_name);
+ free (m_version);
+}
+
+/* Attempt to populate this firehose::generator based on JV, which ought to be a
+ serialization of a firehose.model.Generator python object.
+
+ Return true if successful.
+ Otherwise return false and write an error message to OUT_ERR
+ (which must be freed by the caller). */
+
+bool
+generator::from_json (const json::value *jv, char *&out_err)
+{
+ const char *name;
+ if (!jv->get_string_by_key ("name", name, out_err))
+ return false;
+ m_name = xstrdup (name);
+
+ const char *version = NULL;
+ if (!jv->get_optional_string_by_key ("version", version, out_err))
+ return false;
+ if (version)
+ m_version = xstrdup (version);
+
+ return true;
+}
+
+/* Attempt to populate this firehose::metadata based on JV, which ought to be a
+ serialization of a firehose.model.Metadata python object.
+
+ Return true if successful.
+ Otherwise return false and write an error message to OUT_ERR
+ (which must be freed by the caller). */
+
+bool
+metadata::from_json (const json::value *jv, char *&out_err)
+{
+ const json::value *jv_generator = NULL;
+ if (!jv->get_value_by_key ("generator", jv_generator, out_err))
+ return false;
+ if (!m_generator.from_json (jv_generator, out_err))
+ return false;
+
+ return true;
+}
+
+/* firehose::analysis's dtor. */
+
+analysis::~analysis ()
+{
+ int i;
+ result *result;
+ FOR_EACH_VEC_ELT (m_results, i, result)
+ delete result;
+}
+
+/* Attempt to populate this firehose::analysis based on JV, which ought to be a
+ serialization of a firehose.model.Analysis python object.
+
+ Return true if successful.
+ Otherwise return false and write an error message to OUT_ERR
+ (which must be freed by the caller). */
+
+bool
+analysis::from_json (const json::value *jv, char *&out_err)
+{
+ const json::value *jv_metadata = NULL;
+ if (!jv->get_value_by_key ("metadata", jv_metadata, out_err))
+ return false;
+ if (!m_metadata.from_json (jv_metadata, out_err))
+ return false;
+
+ const json::array *results;
+ if (!jv->get_array_by_key ("results", results, out_err))
+ return false;
+
+ for (unsigned i = 0; i < results->get_length (); i++)
+ {
+ json::value *item = results->get (i);
+ //error ("%s", item->to_str ());
+ result *r = result::from_json (item, out_err);
+ if (!r)
+ return false;
+ m_results.safe_push (r);
+ }
+
+ // FIXME: custom fields
+ // FIXME: selftests for all of this
+
+ return true;
+}
+
+} // namespace firehose
+
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests. */
+
+/* Given JSONFILE, a path relative to SRCDIR/gcc/testsuite/selftests,
+ load the json Firehose file there, populating OUT.
+ Fail if any errors occur. */
+
+static void
+get_analysis (firehose::analysis &out, const char *jsonfile)
+{
+ char *filename = locate_file (jsonfile);
+ char *buffer = selftest::read_file (SELFTEST_LOCATION, filename);
+ ASSERT_TRUE (buffer != NULL);
+ free (filename);
+
+ char *err = NULL;
+ json::value *jv = json::parse_utf8_string (buffer, &err);
+ free (buffer);
+ ASSERT_TRUE (err == NULL);
+ ASSERT_TRUE (jv != NULL);
+
+ //jv->dump(stderr);
+ out.from_json (jv, err);
+ ASSERT_TRUE (err == NULL);
+ delete jv;
+}
+
+/* Parse a sample JSON output generated via the firehose parser for the
+ clang analyzer's plist output, and verify various properties
+ about it. */
+
+static void
+test_parsing_clang_analyzer ()
+{
+ firehose::analysis analysis;
+ get_analysis (analysis, "checker-output/test-clang-analyzer.json");
+
+ ASSERT_STREQ ("clang-analyzer", analysis.m_metadata.m_generator.m_name);
+ ASSERT_EQ (NULL, analysis.m_metadata.m_generator.m_version);
+
+ ASSERT_EQ (1, analysis.m_results.length ());
+ firehose::result *r = analysis.m_results[0];
+ ASSERT_EQ (r->get_kind (), firehose::result::FIREHOSE_ISSUE);
+
+ firehose::issue *issue = (firehose::issue *)r;
+ ASSERT_STREQ ("Address of stack memory associated with"
+ " local variable 'tmp' returned to caller",
+ issue->m_message);
+ ASSERT_EQ (NULL, issue->m_testid);
+
+ ASSERT_LOCEQ ("../../src/bogus.c", 5, 3, issue->m_location);
+
+ ASSERT_TRUE (issue->m_trace != NULL);
+ ASSERT_EQ (3, issue->m_trace->m_states.length ());
+ firehose::state *state0 = issue->m_trace->m_states[0];
+ ASSERT_LOCEQ ("../../src/bogus.c", 3, 3, state0->m_location);
+ ASSERT_EQ (NULL, state0->m_notes);
+
+ firehose::state *state1 = issue->m_trace->m_states[1];
+ ASSERT_LOCEQ ("../../src/bogus.c", 5, 3, state1->m_location);
+ ASSERT_EQ (NULL, state1->m_notes);
+
+ firehose::state *state2 = issue->m_trace->m_states[2];
+ ASSERT_LOCEQ ("../../src/bogus.c", 5, 3, state2->m_location);
+ ASSERT_STREQ ("Address of stack memory associated with"
+ " local variable 'tmp' returned to caller",
+ state2->m_notes);
+ ASSERT_FALSE (issue->m_trace->is_redundant_p (*issue));
+
+ /* Verify filtering out non-textual states from the trace. */
+ issue->m_trace->filter ();
+ ASSERT_EQ (1, issue->m_trace->m_states.length ());
+
+ /* Verify that the filtered trace is redundant. */
+ ASSERT_TRUE (issue->m_trace->is_redundant_p (*issue));
+}
+
+/* Parse a sample JSON output generated via the firehose parser for
+ cppchecks's output, and verify various properties about it. */
+
+static void
+test_parsing_cppcheck ()
+{
+ firehose::analysis analysis;
+ get_analysis (analysis, "checker-output/test-cppcheck.json");
+
+ ASSERT_STREQ ("cppcheck", analysis.m_metadata.m_generator.m_name);
+ ASSERT_STREQ ("1.63", analysis.m_metadata.m_generator.m_version);
+
+ ASSERT_EQ (1, analysis.m_results.length ());
+ firehose::result *r = analysis.m_results[0];
+ ASSERT_EQ (r->get_kind (), firehose::result::FIREHOSE_ISSUE);
+
+ firehose::issue *issue = (firehose::issue *)r;
+ ASSERT_STREQ ("Memory leak: ptr_1", issue->m_message);
+ ASSERT_STREQ ("memleak", issue->m_testid);
+
+ ASSERT_LOCEQ ("../../src/test-sources/conditional-leak.c", 11, 0,
+ issue->m_location);
+
+ ASSERT_TRUE (issue->m_trace == NULL);
+}
+
+/* Parse a JSON file describing a failure to run a checker, and verify
+ various properties about it. */
+
+static void
+test_parsing_failure ()
+{
+ firehose::analysis analysis;
+ get_analysis (analysis, "checker-output/test-failure.json");
+
+ ASSERT_STREQ ("always-fails", analysis.m_metadata.m_generator.m_name);
+
+ ASSERT_EQ (1, analysis.m_results.length ());
+ firehose::result *r = analysis.m_results[0];
+ ASSERT_EQ (r->get_kind (), firehose::result::FIREHOSE_FAILURE);
+
+ firehose::failure *failure = (firehose::failure *)r;
+ ASSERT_STREQ ("Exception running always-fails: [Errno 2]"
+ " No such file or directory:"
+ " '/this/executable/does/not/exist'", failure->m_message);
+ ASSERT_STREQ ("exception", failure->m_failureid);
+}
+
+/* Run all of the selftests within this file. */
+
+void
+firehose_cc_tests ()
+{
+ test_parsing_clang_analyzer ();
+ test_parsing_cppcheck ();
+ //test_parsing_info ();
+ test_parsing_failure ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
new file mode 100644
@@ -0,0 +1,199 @@
+/* Serialization format for checker results.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_FIREHOSE_H
+#define GCC_FIREHOSE_H
+
+/* "Firehose" is a serialization format for results from code
+ analysis tools:
+
+ http://firehose.readthedocs.io/en/latest/index.html
+
+ (along with a Python module for working with the format).
+
+ This file implements a set of C++ classes modeling the format,
+ with support for populating them from a JSON dump, so that we
+ can lossly serialize diagnostics and other static analysis results. */
+
+#include "json.h"
+
+namespace firehose {
+
+/* Forward decls. */
+
+struct issue;
+
+/* A state within a firehose::trace. */
+
+struct state
+{
+ state ();
+ ~state ();
+
+ static state *from_json (const json::value *jv, char *&out_err);
+
+ location_t m_location;
+ char *m_notes;
+};
+
+/* An optional list of events within an issue that describe the circumstances
+ leading up to a problem. */
+
+struct trace
+{
+ ~trace ();
+
+ static trace *from_json (const json::value *jv, char *&out_err);
+
+ void filter ();
+
+ /* If we're just left with a single state that duplicates what we
+ already printed for the issue, don't bother printing it. */
+ bool is_redundant_p (const issue& issue) const;
+
+ auto_vec <state *> m_states;
+};
+
+/* firehose::result is a base class.
+
+ There are three subclasses:
+
+ - a firehose::issue represents a report from an analyzer about a possible
+ problem with the software under test.
+ - a firehose::info represents additional kinds of information generated by
+ an analyzer that isn't a problem per-se e.g. code metrics, licensing info,
+ etc.
+ - a firehose::failure represents a report about a failure of the analyzer
+ itself (e.g. if the analyzer crashed). */
+
+struct result
+{
+ enum kind
+ {
+ FIREHOSE_ISSUE,
+ FIREHOSE_INFO,
+ FIREHOSE_FAILURE
+ };
+
+ result ();
+ virtual ~result ();
+
+ static result *from_json (const json::value *jv, char *&out_err);
+
+ virtual enum kind get_kind () const = 0;
+
+ char *m_message;
+ location_t m_location;
+};
+
+/* An issue represents a report from an analyzer about a possible problem
+ with the software under test. */
+
+struct issue : public result
+{
+ issue ();
+ ~issue ();
+
+ static issue *from_json (const json::value *jv, char *&out_err);
+ enum kind get_kind () const FINAL OVERRIDE { return FIREHOSE_ISSUE; }
+
+ char *m_testid;
+ trace *m_trace;
+};
+
+/* An info represents additional kinds of information generated by an analyzer
+ that isn't a problem per-se e.g. code metrics, licensing info,
+ cross-referencing information, etc. */
+
+struct info : public result
+{
+ info ();
+ ~info ();
+
+ static info *from_json (const json::value *jv, char *&out_err);
+ enum kind get_kind () const FINAL OVERRIDE { return FIREHOSE_INFO; }
+
+ char *m_infoid;
+};
+
+/* A failure represents a report about a failure of the analyzer itself
+ (e.g. if the analyzer crashed).
+
+ If any of these are present then we don't have full coverage.
+
+ For some analyzers this is an all-or-nothing affair: we either get
+ issues reported, or a failure happens (e.g. a segfault of the
+ analysis tool).
+
+ Other analyzers may be more fine-grained: able to report some
+ issues, but choke on some subset of the code under analysis.
+ For example cpychecker runs once per function, and any unhandled
+ Python exceptions only affect one function. */
+
+struct failure : public result
+{
+ failure ();
+ ~failure ();
+
+ static failure *from_json (const json::value *jv, char *&out_err);
+ enum kind get_kind () const FINAL OVERRIDE { return FIREHOSE_FAILURE; }
+
+ char *m_failureid;
+};
+
+/* A class describing a static analyzer, for use within firehose::metadata. */
+
+struct generator
+{
+ generator ();
+ ~generator ();
+
+ bool from_json (const json::value *jv, char *&out_err);
+
+ char *m_name;
+ char *m_version;
+};
+
+/* The firehose::metadata class contains metadata about a static analyzer
+ invocation. */
+
+struct metadata
+{
+ bool from_json (const json::value *jv, char *&out_err);
+
+ generator m_generator;
+};
+
+/* The firehose::analysis class represents one invocation of a code analysis
+ tool. */
+
+struct analysis
+{
+ ~analysis ();
+
+ bool from_json (const json::value *jv, char *&out_err);
+
+ metadata m_metadata;
+ auto_vec<result *> m_results;
+ //custom_fields *m_custom_fields;
+};
+
+} // namespace firehose
+
+#endif /* GCC_FIREHOSE_H */
@@ -74,6 +74,7 @@ selftest::run_tests ()
gimple_c_tests ();
rtl_tests_c_tests ();
read_rtl_function_c_tests ();
+ firehose_cc_tests ();
/* Higher-level tests, or for components that other selftests don't
rely on. */
@@ -177,6 +177,7 @@ extern void edit_context_c_tests ();
extern void et_forest_c_tests ();
extern void fold_const_c_tests ();
extern void fibonacci_heap_c_tests ();
+extern void firehose_cc_tests ();
extern void function_tests_c_tests ();
extern void gimple_c_tests ();
extern void ggc_tests_c_tests ();
new file mode 100644
@@ -0,0 +1,122 @@
+{
+ "customfields": {
+ "scan-build-invocation": "scan-build -v -plist --use-analyzer /usr/bin/clang -o /tmp/tmp8ytuRj gcc -B. -c ../../src/bogus.c",
+ "returncode": 0,
+ "stdout": "scan-build: Using '/usr/bin/clang' for static analysis\nscan-build: Emitting reports for this run to '/tmp/tmp8ytuRj/2017-05-24-001755-39710-1'.\nscan-build: Analysis run complete.\nscan-build: Analysis results (plist files) deposited in '/tmp/tmp8ytuRj/2017-05-24-001755-39710-1'\n",
+ "stderr": "../../src/bogus.c: In function \u2018test\u2019:\n../../src/bogus.c:5:10: warning: function returns address of local variable [-Wreturn-local-addr]\n return tmp;\n ^~~\n../../src/bogus.c:5:3: warning: Address of stack memory associated with local variable 'tmp' returned to caller\n return tmp;\n ^~~~~~~~~~\n1 warning generated.\n",
+ "plistpath": "/tmp/tmp8ytuRj/2017-05-24-001755-39710-1/report-DEoPmt.plist"
+ },
+ "results": [
+ {
+ "severity": null,
+ "trace": {
+ "states": [
+ {
+ "notes": null,
+ "location": {
+ "function": {
+ "name": ""
+ },
+ "range_": {
+ "start": {
+ "column": 3,
+ "line": 3
+ },
+ "end": {
+ "column": 6,
+ "line": 3
+ }
+ },
+ "file": {
+ "abspath": null,
+ "givenpath": "../../src/bogus.c",
+ "hash_": null
+ },
+ "point": null
+ }
+ },
+ {
+ "notes": null,
+ "location": {
+ "function": {
+ "name": ""
+ },
+ "range_": {
+ "start": {
+ "column": 3,
+ "line": 5
+ },
+ "end": {
+ "column": 8,
+ "line": 5
+ }
+ },
+ "file": {
+ "abspath": null,
+ "givenpath": "../../src/bogus.c",
+ "hash_": null
+ },
+ "point": null
+ }
+ },
+ {
+ "notes": {
+ "text": "Address of stack memory associated with local variable 'tmp' returned to caller"
+ },
+ "location": {
+ "function": {
+ "name": ""
+ },
+ "range_": null,
+ "file": {
+ "abspath": null,
+ "givenpath": "../../src/bogus.c",
+ "hash_": null
+ },
+ "point": {
+ "column": 3,
+ "line": 5
+ }
+ }
+ }
+ ]
+ },
+ "type": "Issue",
+ "notes": null,
+ "testid": null,
+ "message": {
+ "text": "Address of stack memory associated with local variable 'tmp' returned to caller"
+ },
+ "cwe": null,
+ "customfields": null,
+ "location": {
+ "function": null,
+ "range_": null,
+ "file": {
+ "abspath": null,
+ "givenpath": "../../src/bogus.c",
+ "hash_": null
+ },
+ "point": {
+ "column": 3,
+ "line": 5
+ }
+ }
+ }
+ ],
+ "metadata": {
+ "stats": {
+ "wallclocktime": 0.22788214683532715
+ },
+ "sut": null,
+ "file_": {
+ "abspath": "/home/david/coding-3/gcc-git-static-analysis/build/gcc/../../src/bogus.c",
+ "givenpath": "../../src/bogus.c",
+ "hash_": null
+ },
+ "generator": {
+ "version": null,
+ "name": "clang-analyzer"
+ }
+ }
+}
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,50 @@
+{
+ "customfields": {
+ "cppcheck-invocation": "cppcheck --xml --xml-version=2 ../../src/test-sources/conditional-leak.c",
+ "returncode": 0,
+ "stdout": "Checking ../../src/test-sources/conditional-leak.c...\n",
+ "stderr": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<results version=\"2\">\n <cppcheck version=\"1.63\"/>\n <errors>\n <error id=\"memleak\" severity=\"error\" msg=\"Memory leak: ptr_1\" verbose=\"Memory leak: ptr_1\">\n <location file=\"../../src/test-sources/conditional-leak.c\" line=\"11\"/>\n </error>\n </errors>\n</results>\n"
+ },
+ "results": [
+ {
+ "severity": "error",
+ "trace": null,
+ "type": "Issue",
+ "notes": null,
+ "testid": "memleak",
+ "message": {
+ "text": "Memory leak: ptr_1"
+ },
+ "cwe": null,
+ "customfields": null,
+ "location": {
+ "function": null,
+ "range_": null,
+ "file": {
+ "abspath": null,
+ "givenpath": "../../src/test-sources/conditional-leak.c",
+ "hash_": null
+ },
+ "point": {
+ "column": 0,
+ "line": 11
+ }
+ }
+ }
+ ],
+ "metadata": {
+ "stats": {
+ "wallclocktime": 0.006749868392944336
+ },
+ "sut": null,
+ "file_": {
+ "abspath": "/home/david/coding-3/gcc-git-static-analysis/build/gcc/../../src/test-sources/conditional-leak.c",
+ "givenpath": "../../src/test-sources/conditional-leak.c",
+ "hash_": null
+ },
+ "generator": {
+ "version": "1.63",
+ "name": "cppcheck"
+ }
+ }
+}
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,38 @@
+{
+ "customfields": {
+ "traceback": "Traceback (most recent call last):\n File \"/home/david/coding-3/gcc-git-static-analysis/src/checkers/checker.py\", line 142, in checked_invoke\n analysis = self.raw_invoke(gccinv, sourcefile)\n File \"./checkers/always_fails.py\", line 40, in raw_invoke\n return self._run_subprocess(sourcefile, args)\n File \"/home/david/coding-3/gcc-git-static-analysis/src/checkers/checker.py\", line 213, in _run_subprocess\n stdout=PIPE, stderr=PIPE, env=env)\n File \"/usr/lib64/python2.7/site-packages/subprocess32.py\", line 812, in __init__\n restore_signals, start_new_session)\n File \"/usr/lib64/python2.7/site-packages/subprocess32.py\", line 1557, in _execute_child\n raise child_exception_type(errno_num, err_msg)\nOSError: [Errno 2] No such file or directory: '/this/executable/does/not/exist'\n"
+ },
+ "results": [
+ {
+ "type": "Failure",
+ "message": {
+ "text": "Exception running always-fails: [Errno 2] No such file or directory: '/this/executable/does/not/exist'"
+ },
+ "failureid": "exception",
+ "location": {
+ "function": null,
+ "range_": null,
+ "file": {
+ "abspath": null,
+ "givenpath": "checkers/test-sources/harmless.c",
+ "hash_": null
+ },
+ "point": null
+ },
+ "customfields": null
+ }
+ ],
+ "metadata": {
+ "stats": null,
+ "sut": null,
+ "file_": {
+ "abspath": "/home/david/coding-3/gcc-git-static-analysis/src/checkers/test-sources/harmless.c",
+ "givenpath": "checkers/test-sources/harmless.c",
+ "hash_": null
+ },
+ "generator": {
+ "version": null,
+ "name": "always-fails"
+ }
+ }
+}
\ No newline at end of file