[gobject-introspection] giscanner: Respect __GI_SCANNER__ when scanning for macros



commit 917da2d1aeffdc884a5fd5cf5b38a60ef088d0fb
Author: Stef Walter <stefw gnome org>
Date:   Fri Apr 19 16:18:40 2013 +0200

    giscanner: Respect __GI_SCANNER__ when scanning for macros
    
    When scanning for macros respect ifdefs of __GI_SCANNER__
    in the various header files. Only #ifdef and #ifndef are supported.
    If __GI_SCANNER__ appears in plain #if statements, a warning is
    printed.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=698367

 giscanner/scannerlexer.l               |   10 ++-
 giscanner/scannerparser.y              |  157 +++++++++++++++++++++++++++++++-
 giscanner/sourcescanner.c              |   23 +++++
 giscanner/sourcescanner.h              |    4 +
 tests/scanner/Regress-1.0-expected.gir |   10 ++
 tests/scanner/regress.h                |   12 +++
 6 files changed, 213 insertions(+), 3 deletions(-)
---
diff --git a/giscanner/scannerlexer.l b/giscanner/scannerlexer.l
index f30191d..7734938 100644
--- a/giscanner/scannerlexer.l
+++ b/giscanner/scannerlexer.l
@@ -80,6 +80,14 @@ stringtext                           ([^\\\"])|(\\.)
 
 "#define "[a-zA-Z_][a-zA-Z_0-9]*"("    { yyless (yyleng - 1); return FUNCTION_MACRO; }
 "#define "[a-zA-Z_][a-zA-Z_0-9]*       { return OBJECT_MACRO; }
+"#ifdef"[\t ]+"__GI_SCANNER__"[\t ]?.*"\n" { return IFDEF_GI_SCANNER; }
+"#ifndef"[\t ]+"__GI_SCANNER__"[\t ]?.*"\n" { return IFNDEF_GI_SCANNER; }
+"#ifndef ".*"\n"                       { return IFNDEF_COND; }
+"#ifdef ".*"\n"                                { return IFDEF_COND; }
+"#if ".*"\n"                           { return IF_COND; }
+"#elif ".*"\n"                         { return ELIF_COND; }
+"#else".*"\n"                          { return ELSE_COND; }
+"#endif".*"\n"                         { return ENDIF_COND; }
 "#pragma ".*"\n"                       { /* Ignore pragma. */ }
 
 "# "[0-9]+" ".*"\n"                    { process_linemarks(scanner); }
@@ -269,7 +277,7 @@ parse_comment (GISourceScanner *scanner)
     comment->line = comment_lineno;
     comment->filename = g_file_get_parse_name (scanner->current_file);
 
-    scanner->comments = g_slist_prepend (scanner->comments, comment);
+    gi_source_scanner_take_comment (scanner, comment);
   } else {
     /*
      * Ignore all other comment blocks
diff --git a/giscanner/scannerparser.y b/giscanner/scannerparser.y
index d440fd3..faf7341 100644
--- a/giscanner/scannerparser.y
+++ b/giscanner/scannerparser.y
@@ -128,6 +128,84 @@ out:
   return dest;
 }
 
+enum {
+  IRRELEVANT = 1,
+  NOT_GI_SCANNER = 2,
+  FOR_GI_SCANNER = 3,
+};
+
+static void
+update_skipping (GISourceScanner *scanner)
+{
+  GList *l;
+  for (l = scanner->conditionals.head; l != NULL; l = g_list_next (l))
+    {
+      if (GPOINTER_TO_INT (l->data) == NOT_GI_SCANNER)
+        {
+           scanner->skipping = TRUE;
+           return;
+        }
+    }
+
+  scanner->skipping = FALSE;
+}
+
+static void
+push_conditional (GISourceScanner *scanner,
+                  gint type)
+{
+  g_assert (type != 0);
+  g_queue_push_head (&scanner->conditionals, GINT_TO_POINTER (type));
+}
+
+static gint
+pop_conditional (GISourceScanner *scanner)
+{
+  gint type = GPOINTER_TO_INT (g_queue_pop_head (&scanner->conditionals));
+
+  if (type == 0)
+    {
+      gchar *filename = g_file_get_path (scanner->current_file);
+      fprintf (stderr, "%s:%d: mismatched %s", filename, lineno, yytext);
+      g_free (filename);
+    }
+
+  return type;
+}
+
+static void
+warn_if_cond_has_gi_scanner (GISourceScanner *scanner,
+                             const gchar *text)
+{
+  /* Some other conditional that is not __GI_SCANNER__ */
+  if (strstr (text, "__GI_SCANNER__"))
+    {
+      gchar *filename = g_file_get_path (scanner->current_file);
+      fprintf (stderr, "%s:%d: the __GI_SCANNER__ constant should only be used with simple #ifdef or #endif: 
%s",
+               filename, lineno, text);
+      g_free (filename);
+    }
+}
+
+static void
+toggle_conditional (GISourceScanner *scanner)
+{
+  switch (pop_conditional (scanner))
+    {
+    case FOR_GI_SCANNER:
+      push_conditional (scanner, NOT_GI_SCANNER);
+      break;
+    case NOT_GI_SCANNER:
+      push_conditional (scanner, FOR_GI_SCANNER);
+      break;
+    case 0:
+      break;
+    default:
+      push_conditional (scanner, IRRELEVANT);
+      break;
+    }
+}
+
 %}
 
 %error-verbose
@@ -160,6 +238,8 @@ out:
 %token VOID VOLATILE WHILE
 
 %token FUNCTION_MACRO OBJECT_MACRO
+%token IFDEF_GI_SCANNER IFNDEF_GI_SCANNER
+%token IFDEF_COND IFNDEF_COND IF_COND ELIF_COND ELSE_COND ENDIF_COND
 
 %start translation_unit
 
@@ -1403,9 +1483,55 @@ object_macro_define
          }
        ;
 
+preproc_conditional
+       : IFDEF_GI_SCANNER 
+         {
+               push_conditional (scanner, FOR_GI_SCANNER);
+               update_skipping (scanner);
+         }
+       | IFNDEF_GI_SCANNER
+         {
+               push_conditional (scanner, NOT_GI_SCANNER);
+               update_skipping (scanner);
+         }
+       | IFDEF_COND 
+         {
+               warn_if_cond_has_gi_scanner (scanner, yytext);
+               push_conditional (scanner, IRRELEVANT);
+         }
+       | IFNDEF_COND
+         {
+               warn_if_cond_has_gi_scanner (scanner, yytext);
+               push_conditional (scanner, IRRELEVANT);
+         }
+       | IF_COND 
+         {
+               warn_if_cond_has_gi_scanner (scanner, yytext);
+               push_conditional (scanner, IRRELEVANT);
+         }
+       | ELIF_COND
+         {
+               warn_if_cond_has_gi_scanner (scanner, yytext);
+               pop_conditional (scanner);
+               push_conditional (scanner, IRRELEVANT);
+               update_skipping (scanner);
+         }
+       | ELSE_COND
+         {
+               toggle_conditional (scanner);
+               update_skipping (scanner);
+         }
+       | ENDIF_COND
+         {
+               pop_conditional (scanner);
+               update_skipping (scanner);
+         }
+       ;
+
 macro
        : function_macro_define
        | object_macro_define
+       | preproc_conditional
        | error
        ;
 
@@ -1435,14 +1561,19 @@ eat_hspace (FILE * f)
 }
 
 static int
-eat_line (FILE * f, int c)
+pass_line (FILE * f, int c,
+           FILE *out)
 {
   while (c != EOF && c != '\n')
     {
+      if (out)
+        fputc (c, out);
       c = fgetc (f);
     }
   if (c == '\n')
     {
+      if (out)
+        fputc (c, out);
       c = fgetc (f);
       if (c == ' ' || c == '\t')
         {
@@ -1453,6 +1584,12 @@ eat_line (FILE * f, int c)
 }
 
 static int
+eat_line (FILE * f, int c)
+{
+  return pass_line (f, c, NULL);
+}
+
+static int
 read_identifier (FILE * f, int c, char **identifier)
 {
   GString *id = g_string_new ("");
@@ -1484,6 +1621,7 @@ gi_source_scanner_parse_macros (GISourceScanner *scanner, GList *filenames)
       GString *define_line;
       char *str;
       gboolean error_line = FALSE;
+      gboolean end_of_word;
       int c = eat_hspace (f);
       while (c != EOF)
         {
@@ -1502,7 +1640,22 @@ gi_source_scanner_parse_macros (GISourceScanner *scanner, GList *filenames)
 
           c = eat_hspace (f);
           c = read_identifier (f, c, &str);
-          if (strcmp (str, "define") != 0 || (c != ' ' && c != '\t'))
+          end_of_word = (c == ' ' || c == '\t' || c == '\n' || c == EOF);
+          if (end_of_word &&
+              (g_str_equal (str, "if") ||
+               g_str_equal (str, "endif") ||
+               g_str_equal (str, "ifndef") ||
+               g_str_equal (str, "ifdef") ||
+               g_str_equal (str, "else") ||
+               g_str_equal (str, "elif")))
+            {
+              fprintf (fmacros, "#%s ", str);
+              g_free (str);
+              c = pass_line (f, c, fmacros);
+              line++;
+              continue;
+            }
+          else if (strcmp (str, "define") != 0 || !end_of_word)
             {
               g_free (str);
               /* ignore line */
diff --git a/giscanner/sourcescanner.c b/giscanner/sourcescanner.c
index 70eca8d..30c2b03 100644
--- a/giscanner/sourcescanner.c
+++ b/giscanner/sourcescanner.c
@@ -221,6 +221,7 @@ gi_source_scanner_new (void)
 
   scanner->files = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal,
                                           g_object_unref, NULL);
+  g_queue_init (&scanner->conditionals);
   return scanner;
 }
 
@@ -247,6 +248,7 @@ gi_source_scanner_free (GISourceScanner *scanner)
 
   g_hash_table_unref (scanner->files);
 
+  g_queue_clear (&scanner->conditionals);
 }
 
 gboolean
@@ -268,6 +270,12 @@ void
 gi_source_scanner_add_symbol (GISourceScanner  *scanner,
                              GISourceSymbol   *symbol)
 {
+  if (scanner->skipping)
+    {
+      g_debug ("skipping symbol due to __GI_SCANNER__ cond: %s", symbol->ident);
+      return;
+    }
+
   g_assert (scanner->current_file);
 
   if (scanner->macro_scan || g_hash_table_contains (scanner->files, scanner->current_file))
@@ -295,6 +303,21 @@ gi_source_scanner_add_symbol (GISourceScanner  *scanner,
     }
 }
 
+void
+gi_source_scanner_take_comment (GISourceScanner *scanner,
+                                GISourceComment *comment)
+{
+  if (scanner->skipping)
+    {
+      g_debug ("skipping comment due to __GI_SCANNER__ cond");
+      gi_source_comment_free (comment);
+      return;
+    }
+
+  scanner->comments = g_slist_prepend (scanner->comments,
+                                       comment);
+}
+
 GSList *
 gi_source_scanner_get_symbols (GISourceScanner  *scanner)
 {
diff --git a/giscanner/sourcescanner.h b/giscanner/sourcescanner.h
index 2db9bdf..254e43b 100644
--- a/giscanner/sourcescanner.h
+++ b/giscanner/sourcescanner.h
@@ -115,6 +115,8 @@ struct _GISourceScanner
   GSList *comments; /* _GIComment */
   GHashTable *typedef_table;
   GHashTable *struct_or_union_or_enum_table;
+  gboolean skipping;
+  GQueue conditionals;
 };
 
 struct _GISourceSymbol
@@ -168,6 +170,8 @@ GISourceSymbol *    gi_source_symbol_copy              (GISourceSymbol     *symb
 /* Private */
 void                gi_source_scanner_add_symbol       (GISourceScanner  *scanner,
                                                        GISourceSymbol   *symbol);
+void                gi_source_scanner_take_comment     (GISourceScanner *scanner,
+                                                        GISourceComment *comment);
 gboolean            gi_source_scanner_is_typedef       (GISourceScanner  *scanner,
                                                        const char       *name);
 void                gi_source_symbol_merge_type        (GISourceSymbol   *symbol,
diff --git a/tests/scanner/Regress-1.0-expected.gir b/tests/scanner/Regress-1.0-expected.gir
index 637a273..bb83e22 100644
--- a/tests/scanner/Regress-1.0-expected.gir
+++ b/tests/scanner/Regress-1.0-expected.gir
@@ -1911,6 +1911,16 @@ exposed to language bindings.</doc>
         </parameter>
       </parameters>
     </callback>
+    <constant name="GI_SCANNER_ELSE"
+              value="3"
+              c:type="REGRESS_GI_SCANNER_ELSE">
+      <type name="gint" c:type="gint"/>
+    </constant>
+    <constant name="GI_SCANNER_IFDEF"
+              value="3"
+              c:type="REGRESS_GI_SCANNER_IFDEF">
+      <type name="gint" c:type="gint"/>
+    </constant>
     <constant name="GUINT64_CONSTANT"
               value="18446744073709551615"
               c:type="REGRESS_GUINT64_CONSTANT">
diff --git a/tests/scanner/regress.h b/tests/scanner/regress.h
index 274d0e4..9c85884 100644
--- a/tests/scanner/regress.h
+++ b/tests/scanner/regress.h
@@ -950,4 +950,16 @@ typedef struct {
 #define REGRESS_MININT64 ((gint64) G_GINT64_CONSTANT(0x8000000000000000))
 #define REGRESS_MAXUINT64 (G_GINT64_CONSTANT(0xffffffffffffffffU))
 
+/* https://bugzilla.gnome.org/show_bug.cgi?id=698367 */
+#ifndef __GI_SCANNER__
+#define REGRESS_DONTSCANTHIS 1
+#else
+#define REGRESS_GI_SCANNER_ELSE 3
+#endif
+#ifndef BLAH
+#ifdef __GI_SCANNER__
+#define REGRESS_GI_SCANNER_IFDEF 3
+#endif
+#endif
+
 #endif /* __GITESTTYPES_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]