From c753018147d304230c6fba13b54daf03a7b92897 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 6 Jan 2020 15:36:36 -0500
Subject: [PATCH 64/68] FIXME: implement function_set::from_file

---
 gcc/analyzer/function-set.cc                  | 155 ++++++++++++++++++
 gcc/analyzer/function-set.h                   |   6 +
 .../selftests/example-function-set.txt        |   6 +
 3 files changed, 167 insertions(+)
 create mode 100644 gcc/testsuite/selftests/example-function-set.txt

diff --git a/gcc/analyzer/function-set.cc b/gcc/analyzer/function-set.cc
index 1b6b5d9f9c1..7ca49f77c03 100644
--- a/gcc/analyzer/function-set.cc
+++ b/gcc/analyzer/function-set.cc
@@ -22,6 +22,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tree.h"
+#include "cpplib.h"
+#include "diagnostic.h"
 #include "selftest.h"
 #include "analyzer/function-set.h"
 
@@ -29,6 +31,138 @@ along with GCC; see the file COPYING3.  If not see
 
 namespace ana {
 
+/* Anonymous namespace for support code for function_set::from_file.  */
+
+namespace {
+
+/* Subclass of function_set for the result olf function_set::from_file.
+   Owns the underlying vec.  */
+
+class function_set_from_file : public function_set
+{
+public:
+  /* Takes ownership of the vec, but not of the names in the vec;
+     these are allocated within READER.
+
+     Take ownership of READER.  */
+
+  function_set_from_file (auto_vec<const char *> *name_vec,
+			  cpp_reader *reader)
+  : function_set (name_vec->address (), name_vec->length ()),
+    m_name_vec (name_vec), m_reader (reader)
+  {
+  }
+  ~function_set_from_file ()
+  {
+    delete m_name_vec;
+    cpp_destroy (m_reader);
+  }
+
+private:
+  auto_vec <const char *> *m_name_vec;
+  cpp_reader *m_reader;
+};
+
+/* Callback for issuing diagnostics from within function_set::from_file.  */
+
+static bool
+fs_diagnostic (cpp_reader *pfile ATTRIBUTE_UNUSED,
+	       enum cpp_diagnostic_level level ATTRIBUTE_UNUSED,
+	       enum cpp_warning_reason reason ATTRIBUTE_UNUSED,
+	       rich_location *richloc,
+	       const char *msg, va_list *ap)
+{
+  diagnostic_info diagnostic;
+  diagnostic_set_info_translated (&diagnostic, msg, ap,
+				  richloc, DK_ERROR);
+  return diagnostic_report_diagnostic (global_dc, &diagnostic);
+}
+
+/* Subrouting of function_set::from_file.
+   Attempt to open FILENAME and parse it, appending identifiers found within
+   it to OUT_VEC.
+   Return the cpp_reader if successful; NULL if any errors occurred.  */
+
+static cpp_reader *
+read_names_from_file (const char *filename,
+		      auto_vec<const char *> *out_vec)
+{
+  /* Passing ident_hash here means that the strings are added to the
+     shared identifier hash.  */
+  cpp_reader *parser = cpp_create_reader (CLK_GNUC89, NULL, line_table);
+  cpp_callbacks *cb = cpp_get_callbacks (parser);
+  cb->diagnostic = fs_diagnostic;
+
+  cpp_read_main_file (parser, filename);
+  bool ok = true;
+
+  location_t loc;
+  const cpp_token *tok;
+  while (1)
+    {
+      tok = cpp_get_token_with_location (parser, &loc);
+      switch (tok->type)
+	{
+	default:
+	  error_at (tok->src_loc, "unexpected token");
+	  ok = false;
+	  goto done;
+
+	case CPP_NAME:
+	  out_vec->safe_push ((const char *)cpp_token_as_text (parser, tok));
+	  break;
+
+	case CPP_EOF:
+	  goto done;
+	}
+    }
+
+ done:
+  cpp_finish (parser, NULL);
+  if (ok)
+    return parser;
+  else
+    {
+      cpp_destroy (parser);
+      return NULL;
+    }
+}
+
+/* Comparator for use by function_set::from_file.  */
+
+int
+cmp (const void *a, const void *b)
+{
+  const char *p = *(const char * const *)a;
+  const char *q = *(const char * const *)b;
+  return strcmp (p, q);
+}
+
+} /* End of anon namespace for support code for function_set::from_file.  */
+
+/* Open FILENAME and parse it, respecting C and C++-style comments.
+   Create a function_set for all identifiers within it.
+   They do not have to be sorted, but there should be no repeats.
+   If successful, return a new function_set which the caller should delete,
+   otherwise, return NULL.  */
+
+function_set *
+function_set::from_file (const char *filename)
+{
+  auto_vec<const char *> *names = new auto_vec<const char *>;
+  cpp_reader *parser = read_names_from_file (filename, names);
+  if (!parser)
+    {
+      delete names;
+      return NULL;
+    }
+
+  names->qsort (cmp);
+
+  function_set_from_file *result = new function_set_from_file (names, parser);
+  return result;
+}
+
 /* Return true if NAME is within this set.  */
 
 bool
@@ -177,6 +311,26 @@ test_stdio_example ()
   ASSERT_FALSE (fs.contains_name_p ("fgets_WITH_A_PREFIX"));
 }
 
+/* Verify that function_set::from_file works as expected.  */
+
+static void
+test_from_file ()
+{
+  char *real_path = locate_file ("example-function-set.txt");
+  function_set *fs = function_set::from_file (real_path);
+  free (real_path);
+
+  fs->assert_sorted ();
+  fs->assert_sane ();
+
+  ASSERT_EQ (fs->count (), 2);
+
+  ASSERT_FALSE (fs->contains_name_p ("not_present"));
+  ASSERT_TRUE (fs->contains_name_p ("fgets"));
+
+  delete fs;
+}
+
 /* Run all of the selftests within this file.  */
 
 void
@@ -186,6 +340,7 @@ analyzer_function_set_cc_tests ()
   test_odd ();
   test_even ();
   test_stdio_example ();
+  test_from_file ();
 }
 
 } // namespace selftest
diff --git a/gcc/analyzer/function-set.h b/gcc/analyzer/function-set.h
index f12ca72c42c..71a9c7bcd71 100644
--- a/gcc/analyzer/function-set.h
+++ b/gcc/analyzer/function-set.h
@@ -34,12 +34,18 @@ public:
   {
   }
 
+  virtual ~function_set () {}
+
+  static function_set *from_file (const char *);
+
   bool contains_name_p (const char *name) const;
   bool contains_decl_p (tree fndecl) const;
 
   void assert_sorted () const;
   void assert_sane () const;
 
+  size_t count () const { return m_count; }
+
 private:
   const char * const *m_names; // must be sorted
   size_t m_count;
diff --git a/gcc/testsuite/selftests/example-function-set.txt b/gcc/testsuite/selftests/example-function-set.txt
new file mode 100644
index 00000000000..7ee7fffb0e2
--- /dev/null
+++ b/gcc/testsuite/selftests/example-function-set.txt
@@ -0,0 +1,6 @@
+/* Example of a comment.  */
+
+// Note that this example file isn't sorted
+
+fgets
+clearerr_unlocked
-- 
2.21.0

