LCOV - code coverage report
Current view: top level - gcc - diagnostic-format-json.cc (source / functions) Hit Total Coverage
Test: gcc.info Lines: 136 151 90.1 %
Date: 2020-04-04 11:58:09 Functions: 12 13 92.3 %
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-2020 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 "diagnostic-metadata.h"
      27                 :            : #include "json.h"
      28                 :            : #include "selftest.h"
      29                 :            : 
      30                 :            : /* The top-level JSON array of pending diagnostics.  */
      31                 :            : 
      32                 :            : static json::array *toplevel_array;
      33                 :            : 
      34                 :            : /* The JSON object for the current diagnostic group.  */
      35                 :            : 
      36                 :            : static json::object *cur_group;
      37                 :            : 
      38                 :            : /* The JSON array for the "children" array within the current diagnostic
      39                 :            :    group.  */
      40                 :            : 
      41                 :            : static json::array *cur_children_array;
      42                 :            : 
      43                 :            : /* Generate a JSON object for LOC.  */
      44                 :            : 
      45                 :            : json::value *
      46                 :        109 : json_from_expanded_location (location_t loc)
      47                 :            : {
      48                 :        109 :   expanded_location exploc = expand_location (loc);
      49                 :        109 :   json::object *result = new json::object ();
      50                 :        109 :   if (exploc.file)
      51                 :        107 :     result->set ("file", new json::string (exploc.file));
      52                 :        109 :   result->set ("line", new json::integer_number (exploc.line));
      53                 :        109 :   result->set ("column", new json::integer_number (exploc.column));
      54                 :        109 :   return result;
      55                 :            : }
      56                 :            : 
      57                 :            : /* Generate a JSON object for LOC_RANGE.  */
      58                 :            : 
      59                 :            : static json::object *
      60                 :         48 : json_from_location_range (const location_range *loc_range, unsigned range_idx)
      61                 :            : {
      62                 :         48 :   location_t caret_loc = get_pure_location (loc_range->m_loc);
      63                 :            : 
      64                 :         48 :   if (caret_loc == UNKNOWN_LOCATION)
      65                 :            :     return NULL;
      66                 :            : 
      67                 :         48 :   location_t start_loc = get_start (loc_range->m_loc);
      68                 :         48 :   location_t finish_loc = get_finish (loc_range->m_loc);
      69                 :            : 
      70                 :         48 :   json::object *result = new json::object ();
      71                 :         48 :   result->set ("caret", json_from_expanded_location (caret_loc));
      72                 :         48 :   if (start_loc != caret_loc
      73                 :         48 :       && start_loc != UNKNOWN_LOCATION)
      74                 :          3 :     result->set ("start", json_from_expanded_location (start_loc));
      75                 :         48 :   if (finish_loc != caret_loc
      76                 :         48 :       && finish_loc != UNKNOWN_LOCATION)
      77                 :         39 :     result->set ("finish", json_from_expanded_location (finish_loc));
      78                 :            : 
      79                 :         48 :   if (loc_range->m_label)
      80                 :            :     {
      81                 :          0 :       label_text text;
      82                 :          0 :       text = loc_range->m_label->get_text (range_idx);
      83                 :          0 :       if (text.m_buffer)
      84                 :          0 :         result->set ("label", new json::string (text.m_buffer));
      85                 :          0 :       text.maybe_free ();
      86                 :            :     }
      87                 :            : 
      88                 :            :   return result;
      89                 :            : }
      90                 :            : 
      91                 :            : /* Generate a JSON object for HINT.  */
      92                 :            : 
      93                 :            : static json::object *
      94                 :          7 : json_from_fixit_hint (const fixit_hint *hint)
      95                 :            : {
      96                 :          7 :   json::object *fixit_obj = new json::object ();
      97                 :            : 
      98                 :          7 :   location_t start_loc = hint->get_start_loc ();
      99                 :          7 :   fixit_obj->set ("start", json_from_expanded_location (start_loc));
     100                 :          7 :   location_t next_loc = hint->get_next_loc ();
     101                 :          7 :   fixit_obj->set ("next", json_from_expanded_location (next_loc));
     102                 :          7 :   fixit_obj->set ("string", new json::string (hint->get_string ()));
     103                 :            : 
     104                 :          7 :   return fixit_obj;
     105                 :            : }
     106                 :            : 
     107                 :            : /* Generate a JSON object for METADATA.  */
     108                 :            : 
     109                 :            : static json::object *
     110                 :          0 : json_from_metadata (const diagnostic_metadata *metadata)
     111                 :            : {
     112                 :          0 :   json::object *metadata_obj = new json::object ();
     113                 :            : 
     114                 :          0 :   if (metadata->get_cwe ())
     115                 :          0 :     metadata_obj->set ("cwe",
     116                 :          0 :                        new json::integer_number (metadata->get_cwe ()));
     117                 :            : 
     118                 :          0 :   return metadata_obj;
     119                 :            : }
     120                 :            : 
     121                 :            : /* No-op implementation of "begin_diagnostic" for JSON output.  */
     122                 :            : 
     123                 :            : static void
     124                 :         46 : json_begin_diagnostic (diagnostic_context *, diagnostic_info *)
     125                 :            : {
     126                 :         46 : }
     127                 :            : 
     128                 :            : /* Implementation of "end_diagnostic" for JSON output.
     129                 :            :    Generate a JSON object for DIAGNOSTIC, and store for output
     130                 :            :    within current diagnostic group.  */
     131                 :            : 
     132                 :            : static void
     133                 :         46 : json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
     134                 :            :                      diagnostic_t orig_diag_kind)
     135                 :            : {
     136                 :         46 :   json::object *diag_obj = new json::object ();
     137                 :            : 
     138                 :            :   /* Get "kind" of diagnostic.  */
     139                 :         46 :   {
     140                 :         46 :     static const char *const diagnostic_kind_text[] = {
     141                 :            : #define DEFINE_DIAGNOSTIC_KIND(K, T, C) (T),
     142                 :            : #include "diagnostic.def"
     143                 :            : #undef DEFINE_DIAGNOSTIC_KIND
     144                 :            :       "must-not-happen"
     145                 :            :     };
     146                 :            :     /* Lose the trailing ": ".  */
     147                 :         46 :     const char *kind_text = diagnostic_kind_text[diagnostic->kind];
     148                 :         46 :     size_t len = strlen (kind_text);
     149                 :         46 :     gcc_assert (len > 2);
     150                 :         46 :     gcc_assert (kind_text[len - 2] == ':');
     151                 :         46 :     gcc_assert (kind_text[len - 1] == ' ');
     152                 :         46 :     char *rstrip = xstrdup (kind_text);
     153                 :         46 :     rstrip[len - 2] = '\0';
     154                 :         46 :     diag_obj->set ("kind", new json::string (rstrip));
     155                 :         46 :     free (rstrip);
     156                 :            :   }
     157                 :            : 
     158                 :            :   // FIXME: encoding of the message (json::string requires UTF-8)
     159                 :         46 :   diag_obj->set ("message",
     160                 :         46 :                  new json::string (pp_formatted_text (context->printer)));
     161                 :         46 :   pp_clear_output_area (context->printer);
     162                 :            : 
     163                 :         46 :   char *option_text;
     164                 :         46 :   option_text = context->option_name (context, diagnostic->option_index,
     165                 :            :                                       orig_diag_kind, diagnostic->kind);
     166                 :         46 :   if (option_text)
     167                 :            :     {
     168                 :         24 :       diag_obj->set ("option", new json::string (option_text));
     169                 :         24 :       free (option_text);
     170                 :            :     }
     171                 :            : 
     172                 :         46 :   if (context->get_option_url)
     173                 :            :     {
     174                 :         46 :       char *option_url = context->get_option_url (context,
     175                 :            :                                                   diagnostic->option_index);
     176                 :         46 :       if (option_url)
     177                 :            :         {
     178                 :         24 :           diag_obj->set ("option_url", new json::string (option_url));
     179                 :         24 :           free (option_url);
     180                 :            :         }
     181                 :            :     }
     182                 :            : 
     183                 :            :   /* If we've already emitted a diagnostic within this auto_diagnostic_group,
     184                 :            :      then add diag_obj to its "children" array.  */
     185                 :         46 :   if (cur_group)
     186                 :            :     {
     187                 :          8 :       gcc_assert (cur_children_array);
     188                 :          8 :       cur_children_array->append (diag_obj);
     189                 :            :     }
     190                 :            :   else
     191                 :            :     {
     192                 :            :       /* Otherwise, make diag_obj be the top-level object within the group;
     193                 :            :          add a "children" array.  */
     194                 :         38 :       toplevel_array->append (diag_obj);
     195                 :         38 :       cur_group = diag_obj;
     196                 :         38 :       cur_children_array = new json::array ();
     197                 :         38 :       diag_obj->set ("children", cur_children_array);
     198                 :            :     }
     199                 :            : 
     200                 :         46 :   const rich_location *richloc = diagnostic->richloc;
     201                 :            : 
     202                 :         46 :   json::array *loc_array = new json::array ();
     203                 :         46 :   diag_obj->set ("locations", loc_array);
     204                 :            : 
     205                 :         92 :   for (unsigned int i = 0; i < richloc->get_num_locations (); i++)
     206                 :            :     {
     207                 :         46 :       const location_range *loc_range = richloc->get_range (i);
     208                 :         46 :       json::object *loc_obj = json_from_location_range (loc_range, i);
     209                 :         46 :       if (loc_obj)
     210                 :         46 :         loc_array->append (loc_obj);
     211                 :            :     }
     212                 :            : 
     213                 :         46 :   if (richloc->get_num_fixit_hints ())
     214                 :            :     {
     215                 :          7 :       json::array *fixit_array = new json::array ();
     216                 :          7 :       diag_obj->set ("fixits", fixit_array);
     217                 :         14 :       for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
     218                 :            :         {
     219                 :          7 :           const fixit_hint *hint = richloc->get_fixit_hint (i);
     220                 :          7 :           json::object *fixit_obj = json_from_fixit_hint (hint);
     221                 :          7 :           fixit_array->append (fixit_obj);
     222                 :            :         }
     223                 :            :     }
     224                 :            : 
     225                 :            :   /* TODO: tree-ish things:
     226                 :            :      TODO: functions
     227                 :            :      TODO: inlining information
     228                 :            :      TODO: macro expansion information.  */
     229                 :            : 
     230                 :         46 :   if (diagnostic->metadata)
     231                 :            :     {
     232                 :          0 :       json::object *metadata_obj = json_from_metadata (diagnostic->metadata);
     233                 :          0 :       diag_obj->set ("metadata", metadata_obj);
     234                 :            :     }
     235                 :            : 
     236                 :         46 :   const diagnostic_path *path = richloc->get_path ();
     237                 :         46 :   if (path && context->make_json_for_path)
     238                 :            :     {
     239                 :          1 :       json::value *path_value = context->make_json_for_path (context, path);
     240                 :          1 :       diag_obj->set ("path", path_value);
     241                 :            :     }
     242                 :         46 : }
     243                 :            : 
     244                 :            : /* No-op implementation of "begin_group_cb" for JSON output.  */
     245                 :            : 
     246                 :            : static void
     247                 :         38 : json_begin_group (diagnostic_context *)
     248                 :            : {
     249                 :         38 : }
     250                 :            : 
     251                 :            : /* Implementation of "end_group_cb" for JSON output.  */
     252                 :            : 
     253                 :            : static void
     254                 :         26 : json_end_group (diagnostic_context *)
     255                 :            : {
     256                 :         26 :   cur_group = NULL;
     257                 :         26 :   cur_children_array = NULL;
     258                 :         26 : }
     259                 :            : 
     260                 :            : /* Callback for final cleanup for JSON output.  */
     261                 :            : 
     262                 :            : static void
     263                 :         33 : json_final_cb (diagnostic_context *)
     264                 :            : {
     265                 :            :   /* Flush the top-level array.  */
     266                 :         33 :   toplevel_array->dump (stderr);
     267                 :         33 :   fprintf (stderr, "\n");
     268                 :         33 :   delete toplevel_array;
     269                 :         33 :   toplevel_array = NULL;
     270                 :         33 : }
     271                 :            : 
     272                 :            : /* Set the output format for CONTEXT to FORMAT.  */
     273                 :            : 
     274                 :            : void
     275                 :         33 : diagnostic_output_format_init (diagnostic_context *context,
     276                 :            :                                enum diagnostics_output_format format)
     277                 :            : {
     278                 :         33 :   switch (format)
     279                 :            :     {
     280                 :          0 :     default:
     281                 :          0 :       gcc_unreachable ();
     282                 :            :     case DIAGNOSTICS_OUTPUT_FORMAT_TEXT:
     283                 :            :       /* The default; do nothing.  */
     284                 :            :       break;
     285                 :            : 
     286                 :         33 :     case DIAGNOSTICS_OUTPUT_FORMAT_JSON:
     287                 :         33 :       {
     288                 :            :         /* Set up top-level JSON array.  */
     289                 :         33 :         if (toplevel_array == NULL)
     290                 :         33 :           toplevel_array = new json::array ();
     291                 :            : 
     292                 :            :         /* Override callbacks.  */
     293                 :         33 :         context->begin_diagnostic = json_begin_diagnostic;
     294                 :         33 :         context->end_diagnostic = json_end_diagnostic;
     295                 :         33 :         context->begin_group_cb = json_begin_group;
     296                 :         33 :         context->end_group_cb =  json_end_group;
     297                 :         33 :         context->final_cb =  json_final_cb;
     298                 :         33 :         context->print_path = NULL; /* handled in json_end_diagnostic.  */
     299                 :            : 
     300                 :            :         /* The metadata is handled in JSON format, rather than as text.  */
     301                 :         33 :         context->show_cwe = false;
     302                 :            : 
     303                 :            :         /* The option is handled in JSON format, rather than as text.  */
     304                 :         33 :         context->show_option_requested = false;
     305                 :            : 
     306                 :            :         /* Don't colorize the text.  */
     307                 :         33 :         pp_show_color (context->printer) = false;
     308                 :            :       }
     309                 :         33 :       break;
     310                 :            :     }
     311                 :         33 : }
     312                 :            : 
     313                 :            : #if CHECKING_P
     314                 :            : 
     315                 :            : namespace selftest {
     316                 :            : 
     317                 :            : /* We shouldn't call json_from_expanded_location on UNKNOWN_LOCATION,
     318                 :            :    but verify that we handle this gracefully.  */
     319                 :            : 
     320                 :            : static void
     321                 :          2 : test_unknown_location ()
     322                 :            : {
     323                 :          2 :   delete json_from_expanded_location (UNKNOWN_LOCATION);
     324                 :          2 : }
     325                 :            : 
     326                 :            : /* Verify that we gracefully handle attempts to serialize bad
     327                 :            :    compound locations.  */
     328                 :            : 
     329                 :            : static void
     330                 :          2 : test_bad_endpoints ()
     331                 :            : {
     332                 :          2 :   location_t bad_endpoints
     333                 :          2 :     = make_location (BUILTINS_LOCATION,
     334                 :            :                      UNKNOWN_LOCATION, UNKNOWN_LOCATION);
     335                 :            : 
     336                 :          2 :   location_range loc_range;
     337                 :          2 :   loc_range.m_loc = bad_endpoints;
     338                 :          2 :   loc_range.m_range_display_kind = SHOW_RANGE_WITH_CARET;
     339                 :          2 :   loc_range.m_label = NULL;
     340                 :            : 
     341                 :          2 :   json::object *obj = json_from_location_range (&loc_range, 0);
     342                 :            :   /* We should have a "caret" value, but no "start" or "finish" values.  */
     343                 :          2 :   ASSERT_TRUE (obj != NULL);
     344                 :          2 :   ASSERT_TRUE (obj->get ("caret") != NULL);
     345                 :          2 :   ASSERT_TRUE (obj->get ("start") == NULL);
     346                 :          2 :   ASSERT_TRUE (obj->get ("finish") == NULL);
     347                 :          2 :   delete obj;
     348                 :          2 : }
     349                 :            : 
     350                 :            : /* Run all of the selftests within this file.  */
     351                 :            : 
     352                 :            : void
     353                 :          2 : diagnostic_format_json_cc_tests ()
     354                 :            : {
     355                 :          2 :   test_unknown_location ();
     356                 :          2 :   test_bad_endpoints ();
     357                 :          2 : }
     358                 :            : 
     359                 :            : } // namespace selftest
     360                 :            : 
     361                 :            : #endif /* #if CHECKING_P */

Generated by: LCOV version 1.0

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 --enable-host-shared. GCC test suite is run with the built compiler.