LCOV - code coverage report
Current view: top level - gcc - diagnostic-format-json.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 92.5 % 201 186
Test Date: 2024-03-23 14:05:01 Functions: 79.2 % 24 19
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* JSON output for diagnostics
       2                 :             :    Copyright (C) 2018-2024 Free Software Foundation, Inc.
       3                 :             :    Contributed by David Malcolm <dmalcolm@redhat.com>.
       4                 :             : 
       5                 :             : This file is part of GCC.
       6                 :             : 
       7                 :             : GCC is free software; you can redistribute it and/or modify it under
       8                 :             : the terms of the GNU General Public License as published by the Free
       9                 :             : Software Foundation; either version 3, or (at your option) any later
      10                 :             : version.
      11                 :             : 
      12                 :             : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      13                 :             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      14                 :             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      15                 :             : for more details.
      16                 :             : 
      17                 :             : You should have received a copy of the GNU General Public License
      18                 :             : along with GCC; see the file COPYING3.  If not see
      19                 :             : <http://www.gnu.org/licenses/>.  */
      20                 :             : 
      21                 :             : 
      22                 :             : #include "config.h"
      23                 :             : #include "system.h"
      24                 :             : #include "coretypes.h"
      25                 :             : #include "diagnostic.h"
      26                 :             : #include "selftest-diagnostic.h"
      27                 :             : #include "diagnostic-metadata.h"
      28                 :             : #include "json.h"
      29                 :             : #include "selftest.h"
      30                 :             : 
      31                 :             : /* Subclass of diagnostic_output_format for JSON output.  */
      32                 :             : 
      33                 :          53 : class json_output_format : public diagnostic_output_format
      34                 :             : {
      35                 :             : public:
      36                 :          58 :   void on_begin_group () final override
      37                 :             :   {
      38                 :             :     /* No-op.  */
      39                 :          58 :   }
      40                 :          38 :   void on_end_group () final override
      41                 :             :   {
      42                 :          38 :     m_cur_group = nullptr;
      43                 :          38 :     m_cur_children_array = nullptr;
      44                 :          38 :   }
      45                 :             :   void
      46                 :          73 :   on_begin_diagnostic (const diagnostic_info &) final override
      47                 :             :   {
      48                 :             :     /* No-op.  */
      49                 :          73 :   }
      50                 :             :   void
      51                 :             :   on_end_diagnostic (const diagnostic_info &diagnostic,
      52                 :             :                      diagnostic_t orig_diag_kind) final override;
      53                 :           0 :   void on_diagram (const diagnostic_diagram &) final override
      54                 :             :   {
      55                 :             :     /* No-op.  */
      56                 :           0 :   }
      57                 :             : 
      58                 :             : protected:
      59                 :          53 :   json_output_format (diagnostic_context &context,
      60                 :             :                       bool formatted)
      61                 :          53 :   : diagnostic_output_format (context),
      62                 :          53 :     m_toplevel_array (new json::array ()),
      63                 :          53 :     m_cur_group (nullptr),
      64                 :          53 :     m_cur_children_array (nullptr),
      65                 :          53 :     m_formatted (formatted)
      66                 :             :   {
      67                 :          53 :   }
      68                 :             : 
      69                 :             :   /* Flush the top-level array to OUTF.  */
      70                 :             :   void
      71                 :          53 :   flush_to_file (FILE *outf)
      72                 :             :   {
      73                 :          53 :     m_toplevel_array->dump (outf, m_formatted);
      74                 :          53 :     fprintf (outf, "\n");
      75                 :          53 :     delete m_toplevel_array;
      76                 :          53 :     m_toplevel_array = nullptr;
      77                 :          53 :   }
      78                 :             : 
      79                 :             : private:
      80                 :             :   /* The top-level JSON array of pending diagnostics.  */
      81                 :             :   json::array *m_toplevel_array;
      82                 :             : 
      83                 :             :   /* The JSON object for the current diagnostic group.  */
      84                 :             :   json::object *m_cur_group;
      85                 :             : 
      86                 :             :   /* The JSON array for the "children" array within the current diagnostic
      87                 :             :      group.  */
      88                 :             :   json::array *m_cur_children_array;
      89                 :             : 
      90                 :             :   bool m_formatted;
      91                 :             : };
      92                 :             : 
      93                 :             : /* Generate a JSON object for LOC.  */
      94                 :             : 
      95                 :             : json::value *
      96                 :         184 : json_from_expanded_location (diagnostic_context *context, location_t loc)
      97                 :             : {
      98                 :         184 :   expanded_location exploc = expand_location (loc);
      99                 :         184 :   json::object *result = new json::object ();
     100                 :         184 :   if (exploc.file)
     101                 :         180 :     result->set_string ("file", exploc.file);
     102                 :         184 :   result->set_integer ("line", exploc.line);
     103                 :             : 
     104                 :         184 :   const enum diagnostics_column_unit orig_unit = context->m_column_unit;
     105                 :         184 :   struct
     106                 :             :   {
     107                 :             :     const char *name;
     108                 :             :     enum diagnostics_column_unit unit;
     109                 :         184 :   } column_fields[] = {
     110                 :             :     {"display-column", DIAGNOSTICS_COLUMN_UNIT_DISPLAY},
     111                 :             :     {"byte-column", DIAGNOSTICS_COLUMN_UNIT_BYTE}
     112                 :             :   };
     113                 :         184 :   int the_column = INT_MIN;
     114                 :         552 :   for (int i = 0; i != ARRAY_SIZE (column_fields); ++i)
     115                 :             :     {
     116                 :         368 :       context->m_column_unit = column_fields[i].unit;
     117                 :         368 :       const int col = context->converted_column (exploc);
     118                 :         368 :       result->set_integer (column_fields[i].name, col);
     119                 :         368 :       if (column_fields[i].unit == orig_unit)
     120                 :         184 :         the_column = col;
     121                 :             :     }
     122                 :         184 :   gcc_assert (the_column != INT_MIN);
     123                 :         184 :   result->set_integer ("column", the_column);
     124                 :         184 :   context->m_column_unit = orig_unit;
     125                 :         184 :   return result;
     126                 :             : }
     127                 :             : 
     128                 :             : /* Generate a JSON object for LOC_RANGE.  */
     129                 :             : 
     130                 :             : static json::object *
     131                 :          77 : json_from_location_range (diagnostic_context *context,
     132                 :             :                           const location_range *loc_range, unsigned range_idx)
     133                 :             : {
     134                 :          77 :   location_t caret_loc = get_pure_location (loc_range->m_loc);
     135                 :             : 
     136                 :          77 :   if (caret_loc == UNKNOWN_LOCATION)
     137                 :             :     return NULL;
     138                 :             : 
     139                 :          77 :   location_t start_loc = get_start (loc_range->m_loc);
     140                 :          77 :   location_t finish_loc = get_finish (loc_range->m_loc);
     141                 :             : 
     142                 :          77 :   json::object *result = new json::object ();
     143                 :          77 :   result->set ("caret", json_from_expanded_location (context, caret_loc));
     144                 :          77 :   if (start_loc != caret_loc
     145                 :          77 :       && start_loc != UNKNOWN_LOCATION)
     146                 :          15 :     result->set ("start", json_from_expanded_location (context, start_loc));
     147                 :          77 :   if (finish_loc != caret_loc
     148                 :          77 :       && finish_loc != UNKNOWN_LOCATION)
     149                 :          61 :     result->set ("finish", json_from_expanded_location (context, finish_loc));
     150                 :             : 
     151                 :          77 :   if (loc_range->m_label)
     152                 :             :     {
     153                 :           0 :       label_text text (loc_range->m_label->get_text (range_idx));
     154                 :           0 :       if (text.get ())
     155                 :           0 :         result->set_string ("label", text.get ());
     156                 :           0 :     }
     157                 :             : 
     158                 :             :   return result;
     159                 :             : }
     160                 :             : 
     161                 :             : /* Generate a JSON object for HINT.  */
     162                 :             : 
     163                 :             : static json::object *
     164                 :           7 : json_from_fixit_hint (diagnostic_context *context, const fixit_hint *hint)
     165                 :             : {
     166                 :           7 :   json::object *fixit_obj = new json::object ();
     167                 :             : 
     168                 :           7 :   location_t start_loc = hint->get_start_loc ();
     169                 :           7 :   fixit_obj->set ("start", json_from_expanded_location (context, start_loc));
     170                 :           7 :   location_t next_loc = hint->get_next_loc ();
     171                 :           7 :   fixit_obj->set ("next", json_from_expanded_location (context, next_loc));
     172                 :           7 :   fixit_obj->set_string ("string", hint->get_string ());
     173                 :             : 
     174                 :           7 :   return fixit_obj;
     175                 :             : }
     176                 :             : 
     177                 :             : /* Generate a JSON object for METADATA.  */
     178                 :             : 
     179                 :             : static json::object *
     180                 :           5 : json_from_metadata (const diagnostic_metadata *metadata)
     181                 :             : {
     182                 :           5 :   json::object *metadata_obj = new json::object ();
     183                 :             : 
     184                 :           5 :   if (metadata->get_cwe ())
     185                 :           5 :     metadata_obj->set_integer ("cwe", metadata->get_cwe ());
     186                 :             : 
     187                 :           5 :   return metadata_obj;
     188                 :             : }
     189                 :             : 
     190                 :             : /* Implementation of "on_end_diagnostic" vfunc for JSON output.
     191                 :             :    Generate a JSON object for DIAGNOSTIC, and store for output
     192                 :             :    within current diagnostic group.  */
     193                 :             : 
     194                 :             : void
     195                 :          73 : json_output_format::on_end_diagnostic (const diagnostic_info &diagnostic,
     196                 :             :                                        diagnostic_t orig_diag_kind)
     197                 :             : {
     198                 :          73 :   json::object *diag_obj = new json::object ();
     199                 :             : 
     200                 :             :   /* Get "kind" of diagnostic.  */
     201                 :          73 :   {
     202                 :          73 :     static const char *const diagnostic_kind_text[] = {
     203                 :             : #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
     204                 :             : #include "diagnostic.def"
     205                 :             : #undef DEFINE_DIAGNOSTIC_KIND
     206                 :             :       "must-not-happen"
     207                 :             :     };
     208                 :             :     /* Lose the trailing ": ".  */
     209                 :          73 :     const char *kind_text = diagnostic_kind_text[diagnostic.kind];
     210                 :          73 :     size_t len = strlen (kind_text);
     211                 :          73 :     gcc_assert (len > 2);
     212                 :          73 :     gcc_assert (kind_text[len - 2] == ':');
     213                 :          73 :     gcc_assert (kind_text[len - 1] == ' ');
     214                 :          73 :     char *rstrip = xstrdup (kind_text);
     215                 :          73 :     rstrip[len - 2] = '\0';
     216                 :          73 :     diag_obj->set_string ("kind", rstrip);
     217                 :          73 :     free (rstrip);
     218                 :             :   }
     219                 :             : 
     220                 :             :   // FIXME: encoding of the message (json::string requires UTF-8)
     221                 :          73 :   diag_obj->set_string ("message", pp_formatted_text (m_context.printer));
     222                 :          73 :   pp_clear_output_area (m_context.printer);
     223                 :             : 
     224                 :          73 :   if (char *option_text = m_context.make_option_name (diagnostic.option_index,
     225                 :             :                                                       orig_diag_kind,
     226                 :          73 :                                                       diagnostic.kind))
     227                 :             :     {
     228                 :          31 :       diag_obj->set_string ("option", option_text);
     229                 :          31 :       free (option_text);
     230                 :             :     }
     231                 :             : 
     232                 :         146 :   if (char *option_url = m_context.make_option_url (diagnostic.option_index))
     233                 :             :     {
     234                 :          31 :       diag_obj->set_string ("option_url", option_url);
     235                 :          31 :       free (option_url);
     236                 :             :     }
     237                 :             : 
     238                 :             :   /* If we've already emitted a diagnostic within this auto_diagnostic_group,
     239                 :             :      then add diag_obj to its "children" array.  */
     240                 :          73 :   if (m_cur_group)
     241                 :             :     {
     242                 :          15 :       gcc_assert (m_cur_children_array);
     243                 :          15 :       m_cur_children_array->append (diag_obj);
     244                 :             :     }
     245                 :             :   else
     246                 :             :     {
     247                 :             :       /* Otherwise, make diag_obj be the top-level object within the group;
     248                 :             :          add a "children" array and record the column origin.  */
     249                 :          58 :       m_toplevel_array->append (diag_obj);
     250                 :          58 :       m_cur_group = diag_obj;
     251                 :          58 :       m_cur_children_array = new json::array ();
     252                 :          58 :       diag_obj->set ("children", m_cur_children_array);
     253                 :          58 :       diag_obj->set_integer ("column-origin", m_context.m_column_origin);
     254                 :             :     }
     255                 :             : 
     256                 :          73 :   const rich_location *richloc = diagnostic.richloc;
     257                 :             : 
     258                 :          73 :   json::array *loc_array = new json::array ();
     259                 :          73 :   diag_obj->set ("locations", loc_array);
     260                 :             : 
     261                 :         146 :   for (unsigned int i = 0; i < richloc->get_num_locations (); i++)
     262                 :             :     {
     263                 :          73 :       const location_range *loc_range = richloc->get_range (i);
     264                 :          73 :       json::object *loc_obj
     265                 :          73 :         = json_from_location_range (&m_context, loc_range, i);
     266                 :          73 :       if (loc_obj)
     267                 :          73 :         loc_array->append (loc_obj);
     268                 :             :     }
     269                 :             : 
     270                 :          73 :   if (richloc->get_num_fixit_hints ())
     271                 :             :     {
     272                 :           7 :       json::array *fixit_array = new json::array ();
     273                 :           7 :       diag_obj->set ("fixits", fixit_array);
     274                 :          14 :       for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
     275                 :             :         {
     276                 :           7 :           const fixit_hint *hint = richloc->get_fixit_hint (i);
     277                 :           7 :           json::object *fixit_obj = json_from_fixit_hint (&m_context, hint);
     278                 :           7 :           fixit_array->append (fixit_obj);
     279                 :             :         }
     280                 :             :     }
     281                 :             : 
     282                 :             :   /* TODO: tree-ish things:
     283                 :             :      TODO: functions
     284                 :             :      TODO: inlining information
     285                 :             :      TODO: macro expansion information.  */
     286                 :             : 
     287                 :          73 :   if (diagnostic.metadata)
     288                 :             :     {
     289                 :           5 :       json::object *metadata_obj = json_from_metadata (diagnostic.metadata);
     290                 :           5 :       diag_obj->set ("metadata", metadata_obj);
     291                 :             :     }
     292                 :             : 
     293                 :          73 :   const diagnostic_path *path = richloc->get_path ();
     294                 :          73 :   if (path && m_context.m_make_json_for_path)
     295                 :             :     {
     296                 :           6 :       json::value *path_value
     297                 :           6 :         = m_context.m_make_json_for_path (&m_context, path);
     298                 :           6 :       diag_obj->set ("path", path_value);
     299                 :             :     }
     300                 :             : 
     301                 :         146 :   diag_obj->set ("escape-source",
     302                 :         146 :                  new json::literal (richloc->escape_on_output_p ()));
     303                 :          73 : }
     304                 :             : 
     305                 :             : class json_stderr_output_format : public json_output_format
     306                 :             : {
     307                 :             : public:
     308                 :          38 :   json_stderr_output_format (diagnostic_context &context,
     309                 :             :                              bool formatted)
     310                 :          76 :     : json_output_format (context, formatted)
     311                 :             :   {
     312                 :             :   }
     313                 :          38 :   ~json_stderr_output_format ()
     314                 :          38 :   {
     315                 :          38 :     flush_to_file (stderr);
     316                 :          38 :   }
     317                 :           0 :   bool machine_readable_stderr_p () const final override
     318                 :             :   {
     319                 :           0 :     return true;
     320                 :             :   }
     321                 :             : };
     322                 :             : 
     323                 :             : class json_file_output_format : public json_output_format
     324                 :             : {
     325                 :             : public:
     326                 :          15 :   json_file_output_format (diagnostic_context &context,
     327                 :             :                            bool formatted,
     328                 :             :                            const char *base_file_name)
     329                 :          15 :   : json_output_format (context, formatted),
     330                 :          15 :     m_base_file_name (xstrdup (base_file_name))
     331                 :             :   {
     332                 :          15 :   }
     333                 :             : 
     334                 :          30 :   ~json_file_output_format ()
     335                 :          15 :   {
     336                 :          15 :     char *filename = concat (m_base_file_name, ".gcc.json", NULL);
     337                 :          15 :     free (m_base_file_name);
     338                 :          15 :     m_base_file_name = nullptr;
     339                 :          15 :     FILE *outf = fopen (filename, "w");
     340                 :          15 :     if (!outf)
     341                 :             :       {
     342                 :           0 :         const char *errstr = xstrerror (errno);
     343                 :           0 :         fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
     344                 :             :                  filename, errstr);
     345                 :           0 :         free (filename);
     346                 :           0 :         return;
     347                 :             :       }
     348                 :          15 :     flush_to_file (outf);
     349                 :          15 :     fclose (outf);
     350                 :          15 :     free (filename);
     351                 :          30 :   }
     352                 :           0 :   bool machine_readable_stderr_p () const final override
     353                 :             :   {
     354                 :           0 :     return false;
     355                 :             :   }
     356                 :             : 
     357                 :             : private:
     358                 :             :   char *m_base_file_name;
     359                 :             : };
     360                 :             : 
     361                 :             : /* Populate CONTEXT in preparation for JSON output (either to stderr, or
     362                 :             :    to a file).  */
     363                 :             : 
     364                 :             : static void
     365                 :          53 : diagnostic_output_format_init_json (diagnostic_context *context)
     366                 :             : {
     367                 :             :   /* Override callbacks.  */
     368                 :          53 :   context->m_print_path = nullptr; /* handled in json_end_diagnostic.  */
     369                 :             : 
     370                 :             :   /* The metadata is handled in JSON format, rather than as text.  */
     371                 :          53 :   context->set_show_cwe (false);
     372                 :          53 :   context->set_show_rules (false);
     373                 :             : 
     374                 :             :   /* The option is handled in JSON format, rather than as text.  */
     375                 :          53 :   context->set_show_option_requested (false);
     376                 :             : 
     377                 :             :   /* Don't colorize the text.  */
     378                 :          53 :   pp_show_color (context->printer) = false;
     379                 :           0 : }
     380                 :             : 
     381                 :             : /* Populate CONTEXT in preparation for JSON output to stderr.  */
     382                 :             : 
     383                 :             : void
     384                 :          38 : diagnostic_output_format_init_json_stderr (diagnostic_context *context,
     385                 :             :                                            bool formatted)
     386                 :             : {
     387                 :          38 :   diagnostic_output_format_init_json (context);
     388                 :          76 :   context->set_output_format (new json_stderr_output_format (*context,
     389                 :          38 :                                                              formatted));
     390                 :          38 : }
     391                 :             : 
     392                 :             : /* Populate CONTEXT in preparation for JSON output to a file named
     393                 :             :    BASE_FILE_NAME.gcc.json.  */
     394                 :             : 
     395                 :             : void
     396                 :          15 : diagnostic_output_format_init_json_file (diagnostic_context *context,
     397                 :             :                                          bool formatted,
     398                 :             :                                          const char *base_file_name)
     399                 :             : {
     400                 :          15 :   diagnostic_output_format_init_json (context);
     401                 :          15 :   context->set_output_format (new json_file_output_format (*context,
     402                 :             :                                                            formatted,
     403                 :          15 :                                                            base_file_name));
     404                 :          15 : }
     405                 :             : 
     406                 :             : #if CHECKING_P
     407                 :             : 
     408                 :             : namespace selftest {
     409                 :             : 
     410                 :             : /* We shouldn't call json_from_expanded_location on UNKNOWN_LOCATION,
     411                 :             :    but verify that we handle this gracefully.  */
     412                 :             : 
     413                 :             : static void
     414                 :           4 : test_unknown_location ()
     415                 :             : {
     416                 :           4 :   test_diagnostic_context dc;
     417                 :           4 :   delete json_from_expanded_location (&dc, UNKNOWN_LOCATION);
     418                 :           4 : }
     419                 :             : 
     420                 :             : /* Verify that we gracefully handle attempts to serialize bad
     421                 :             :    compound locations.  */
     422                 :             : 
     423                 :             : static void
     424                 :           4 : test_bad_endpoints ()
     425                 :             : {
     426                 :           4 :   location_t bad_endpoints
     427                 :           4 :     = make_location (BUILTINS_LOCATION,
     428                 :             :                      UNKNOWN_LOCATION, UNKNOWN_LOCATION);
     429                 :             : 
     430                 :           4 :   location_range loc_range;
     431                 :           4 :   loc_range.m_loc = bad_endpoints;
     432                 :           4 :   loc_range.m_range_display_kind = SHOW_RANGE_WITH_CARET;
     433                 :           4 :   loc_range.m_label = NULL;
     434                 :             : 
     435                 :           4 :   test_diagnostic_context dc;
     436                 :           4 :   json::object *obj = json_from_location_range (&dc, &loc_range, 0);
     437                 :             :   /* We should have a "caret" value, but no "start" or "finish" values.  */
     438                 :           4 :   ASSERT_TRUE (obj != NULL);
     439                 :           4 :   ASSERT_TRUE (obj->get ("caret") != NULL);
     440                 :           4 :   ASSERT_TRUE (obj->get ("start") == NULL);
     441                 :           4 :   ASSERT_TRUE (obj->get ("finish") == NULL);
     442                 :           4 :   delete obj;
     443                 :           4 : }
     444                 :             : 
     445                 :             : /* Run all of the selftests within this file.  */
     446                 :             : 
     447                 :             : void
     448                 :           4 : diagnostic_format_json_cc_tests ()
     449                 :             : {
     450                 :           4 :   test_unknown_location ();
     451                 :           4 :   test_bad_endpoints ();
     452                 :           4 : }
     453                 :             : 
     454                 :             : } // namespace selftest
     455                 :             : 
     456                 :             : #endif /* #if CHECKING_P */
        

Generated by: LCOV version 2.0-1

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.