From c0f368c5017141a9cdcbbefeae069dd43e6d363d Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 25 May 2017 16:03:14 -0400
Subject: [PATCH 20/28] FIXME: load checker policy files

---
 gcc/checkers.c                                     | 182 +++++++++++++++++++--
 .../selftests/checker-policy/test-policy.json      |   7 +
 2 files changed, 178 insertions(+), 11 deletions(-)
 create mode 100644 gcc/testsuite/selftests/checker-policy/test-policy.json

diff --git a/gcc/checkers.c b/gcc/checkers.c
index 1cb4aeb..a5bae51 100644
--- a/gcc/checkers.c
+++ b/gcc/checkers.c
@@ -234,14 +234,75 @@ auto_argvec::~auto_argvec ()
 
 /* FIXME.  */
 
-static void
-run_one_analyzer (const char *wrapper_path)
+class checker
+{
+ public:
+  checker ();
+  ~checker ();
+
+  static checker *from_json (const json::value *jv, char *&out_err);
+
+  void run ();
+
+  char *m_executable;
+};
+
+/* FIXME.  */
+
+class policy
+{
+ public:
+  ~policy ();
+
+  bool read_from_file (const char *path, char *&out_err);
+
+  auto_vec<checker *> m_checkers;
+};
+
+/* FIXME.  */
+
+checker::checker () : m_executable (NULL)
+{
+}
+
+/* FIXME.  */
+
+checker::~checker ()
+{
+  free (m_executable);
+}
+
+/* FIXME.  */
+
+checker *
+checker::from_json (const json::value *jv, char *&out_err)
+{
+  checker *ch = new checker ();
+
+  const char *executable;
+  if (!jv->get_string_by_key ("executable", executable, out_err))
+    {
+      delete ch;
+      return NULL;
+    }
+  ch->m_executable = xstrdup (executable);
+
+  // FIXME: languages
+
+  return ch;
+}
+
+/* FIXME.  */
+
+void
+checker::run ()
 {
   auto_argvec argvec;
 #define ADD_ARG(arg) argvec.safe_push (xstrdup (arg))
 
-  ADD_ARG (wrapper_path);
+  ADD_ARG (m_executable);
   ADD_ARG ("-B."); // FIXME
+  // FIXME: supply at least -I and -D args
   ADD_ARG ("-c");
   ADD_ARG (main_input_filename);
 
@@ -253,7 +314,7 @@ run_one_analyzer (const char *wrapper_path)
   int err = 0;
   /* FIXME: avoid using known filename; read direct from stdout.  */
   errmsg = pex_one (PEX_SEARCH | PEX_LAST, /* int flags, */
-		    wrapper_path,
+		    m_executable,
 		    const_cast <char *const *> (argvec.address ()),
 		    progname, /* const char *pname */
 		    "temp.json", /* const char *outname */
@@ -263,14 +324,14 @@ run_one_analyzer (const char *wrapper_path)
   if (errmsg)
     {
       error_at (UNKNOWN_LOCATION, "error invoking checker %qs: %qs",
-		wrapper_path, errmsg);
+		m_executable, errmsg);
       return;
     }
   if (exit_status || err)
     {
       error_at (UNKNOWN_LOCATION,
 		"error invoking checker %qs: exit_status: %i err: %i",
-		wrapper_path, exit_status, err);
+		m_executable, exit_status, err);
       return;
     }
 
@@ -286,15 +347,114 @@ run_one_analyzer (const char *wrapper_path)
 
 /* FIXME.  */
 
+policy::~policy ()
+{
+  int i;
+  checker *checker;
+  FOR_EACH_VEC_ELT (m_checkers, i, checker)
+    delete checker;
+}
+
+/* FIXME.  */
+
+bool
+policy::read_from_file (const char *path, char *&out_err)
+{
+  // FIXME: this shouldn't be just in the selftests
+  char *utf8_buffer = selftest::read_file (SELFTEST_LOCATION, path);
+  // FIXME: error-checking
+
+  /* Attempt to parse the buffer as UTF-8-encoded JSON.  */
+  json::value *jv = json::parse_utf8_string (utf8_buffer, &out_err);
+  if (!jv)
+    {
+      free (utf8_buffer);
+      return false;
+    }
+
+  /* Convert to a policy object.  */
+  const json::array *arr = jv->as_array ();
+  if (!arr)
+    {
+      out_err = xstrdup ("not an array");
+      delete jv;
+      return false;
+    }
+
+  for (unsigned i = 0; i < arr->get_length (); i++)
+    {
+      checker *ch = checker::from_json (arr->get (i), out_err);
+      if (!ch)
+	{
+	  delete jv;
+	  return false;
+	}
+      m_checkers.safe_push (ch);
+    }
+
+  delete jv;
+  return true;
+}
+
+/* FIXME.  */
+
 void
 run_analyzers (const char *path)
 {
   if (0)
     inform (UNKNOWN_LOCATION, "run_analyzers (%qs)", path);
 
-  run_one_analyzer ("../../src/checkers/clang_analyzer.py");
-  run_one_analyzer ("../../src/checkers/cppcheck.py");
-  run_one_analyzer ("../../src/checkers/flawfinder.py");
-  run_one_analyzer ("../../src/checkers/always_fails.py");
-  run_one_analyzer ("../../src/checkers/ianal.py");
+  /* Try to load a policy file.  */
+  policy p;
+  char *err = NULL;
+  if (!p.policy::read_from_file (path, err))
+    {
+      error_at (UNKNOWN_LOCATION,
+		"unable to load checker policy %qs: %qs",
+		path, err);
+      free (err);
+      return;
+    }
+
+  /* Run the checkers specified by the policy.  */
+  int i;
+  checker *ch;
+  FOR_EACH_VEC_ELT (p.m_checkers, i, ch)
+    ch->run ();
 }
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+/* FIXME.  */
+
+static void
+test_policy_parsing ()
+{
+  char *filename = locate_file ("checker-policy/test-policy.json");
+  char *err = NULL;
+  policy p;
+  bool success = p.policy::read_from_file (filename, err);
+  ASSERT_TRUE (success);
+  ASSERT_NE (NULL, err);
+  free (filename);
+
+  ASSERT_EQ (4, p.m_checkers.length ());
+  ASSERT_STREQ ("../../src/checkers/clang_analyzer.py",
+		p.m_checkers[0]->m_executable);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+checkers_c_tests ()
+{
+  test_policy_parsing ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/testsuite/selftests/checker-policy/test-policy.json b/gcc/testsuite/selftests/checker-policy/test-policy.json
new file mode 100644
index 0000000..90532b2
--- /dev/null
+++ b/gcc/testsuite/selftests/checker-policy/test-policy.json
@@ -0,0 +1,7 @@
+[{ "executable": "../../src/checkers/clang_analyzer.py",
+   "languages": ["c", "c++"] },
+ { "executable": "../../src/checkers/cppcheck.py",
+   "languages": ["c", "c++"] },
+ { "executable": "../../src/checkers/flawfinder.py",
+   "languages": ["c", "c++"] },
+ { "executable": "../../src/checkers/ianal.py"}]
-- 
1.8.5.3

