LCOV - code coverage report
Current view: top level - gcc/analyzer - analyzer.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 85.6 % 208 178
Test Date: 2026-05-11 19:44:49 Functions: 95.8 % 24 23
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Utility functions for the analyzer.
       2              :    Copyright (C) 2019-2026 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
       8              : under the terms of the GNU General Public License as published by
       9              : the Free Software Foundation; either version 3, or (at your option)
      10              : any later version.
      11              : 
      12              : GCC is distributed in the hope that it will be useful, but
      13              : WITHOUT ANY WARRANTY; without even the implied warranty of
      14              : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15              : General Public License 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              : #include "analyzer/common.h"
      22              : 
      23              : #include "tree-pretty-print.h"
      24              : #include "diagnostics/event-id.h"
      25              : #include "tree-dfa.h"
      26              : #include "intl.h"
      27              : 
      28              : #if ENABLE_ANALYZER
      29              : 
      30              : namespace ana {
      31              : 
      32              : bool
      33           65 : printable_expr_p (const_tree expr)
      34              : {
      35           65 :   if (TREE_CODE (expr) == SSA_NAME
      36           65 :       && !SSA_NAME_VAR (expr))
      37           33 :     return false;
      38              : 
      39              :   return true;
      40              : }
      41              : 
      42              : /* Workaround for missing location information for some stmts,
      43              :    which ultimately should be solved by fixing the frontends
      44              :    to provide the locations (TODO).  */
      45              : 
      46              : location_t
      47       128052 : get_stmt_location (const gimple *stmt, function *fun)
      48              : {
      49       128052 :   if (!stmt)
      50              :     return UNKNOWN_LOCATION;
      51       128052 :   if (get_pure_location (stmt->location) == UNKNOWN_LOCATION)
      52              :     {
      53              :       /* Workaround for missing location information for clobber
      54              :          stmts, which seem to lack location information in the C frontend
      55              :          at least.  Created by gimplify_bind_expr, which uses the
      56              :            BLOCK_SOURCE_END_LOCATION (BIND_EXPR_BLOCK (bind_expr))
      57              :          but this is never set up when the block is created in
      58              :          c_end_compound_stmt's pop_scope.
      59              :          TODO: fix this missing location information.
      60              : 
      61              :          For now, as a hackish workaround, use the location of the end of
      62              :          the function.  */
      63        11481 :       if (gimple_clobber_p (stmt) && fun)
      64         3185 :         return fun->function_end_locus;
      65              :     }
      66              : 
      67       124867 :   return stmt->location;
      68              : }
      69              : 
      70              : static tree
      71              : fixup_tree_for_diagnostic_1 (tree expr, hash_set<tree> *visited);
      72              : 
      73              : /* Attemp to generate a tree for the LHS of ASSIGN_STMT.
      74              :    VISITED must be non-NULL; it is used to ensure termination.  */
      75              : 
      76              : static tree
      77       418857 : get_diagnostic_tree_for_gassign_1 (const gassign *assign_stmt,
      78              :                                    hash_set<tree> *visited)
      79              : {
      80       418857 :   enum tree_code code = gimple_assign_rhs_code (assign_stmt);
      81              : 
      82              :   /* Reverse the effect of extract_ops_from_tree during
      83              :      gimplification.  */
      84       418857 :   switch (get_gimple_rhs_class (code))
      85              :     {
      86            0 :     default:
      87            0 :     case GIMPLE_INVALID_RHS:
      88            0 :       gcc_unreachable ();
      89       218163 :     case GIMPLE_TERNARY_RHS:
      90       218163 :     case GIMPLE_BINARY_RHS:
      91       218163 :     case GIMPLE_UNARY_RHS:
      92       218163 :       {
      93       218163 :         tree t = make_node (code);
      94       218163 :         TREE_TYPE (t) = TREE_TYPE (gimple_assign_lhs (assign_stmt));
      95       218163 :         unsigned num_rhs_args = gimple_num_ops (assign_stmt) - 1;
      96       567472 :         for (unsigned i = 0; i < num_rhs_args; i++)
      97              :           {
      98       349309 :             tree op = gimple_op (assign_stmt, i + 1);
      99       349309 :             if (op)
     100              :               {
     101       349309 :                 op = fixup_tree_for_diagnostic_1 (op, visited);
     102       349309 :                 if (op == NULL_TREE)
     103              :                   return NULL_TREE;
     104              :               }
     105       349309 :             TREE_OPERAND (t, i) = op;
     106              :           }
     107              :         return t;
     108              :       }
     109       200694 :     case GIMPLE_SINGLE_RHS:
     110       200694 :       {
     111       200694 :         tree op = gimple_op (assign_stmt, 1);
     112       200694 :         op = fixup_tree_for_diagnostic_1 (op, visited);
     113       200694 :         return op;
     114              :       }
     115              :     }
     116              : }
     117              : 
     118              : /*  Subroutine of fixup_tree_for_diagnostic_1, called on SSA names.
     119              :     Attempt to reconstruct a tree expression for SSA_NAME
     120              :     based on its def-stmt.
     121              :     SSA_NAME must be non-NULL.
     122              :     VISITED must be non-NULL; it is used to ensure termination.
     123              : 
     124              :     Return NULL_TREE if there is a problem.  */
     125              : 
     126              : static tree
     127       191896 : maybe_reconstruct_from_def_stmt (tree ssa_name,
     128              :                                  hash_set<tree> *visited)
     129              : {
     130              :   /* Ensure termination.  */
     131       191896 :   if (visited->contains (ssa_name))
     132              :     return NULL_TREE;
     133       191845 :   visited->add (ssa_name);
     134              : 
     135       191845 :   gimple *def_stmt = SSA_NAME_DEF_STMT (ssa_name);
     136              : 
     137       191845 :   switch (gimple_code (def_stmt))
     138              :     {
     139            0 :     default:
     140            0 :       gcc_unreachable ();
     141              :     case GIMPLE_ASM:
     142              :     case GIMPLE_NOP:
     143              :     case GIMPLE_PHI:
     144              :       /* Can't handle these.  */
     145              :       return NULL_TREE;
     146       173463 :     case GIMPLE_ASSIGN:
     147       173463 :       return get_diagnostic_tree_for_gassign_1
     148       173463 :         (as_a <const gassign *> (def_stmt), visited);
     149        16626 :     case GIMPLE_CALL:
     150        16626 :       {
     151        16626 :         gcall *call_stmt = as_a <gcall *> (def_stmt);
     152        16626 :         tree return_type = gimple_call_return_type (call_stmt);
     153        16626 :         tree fn = fixup_tree_for_diagnostic_1 (gimple_call_fn (call_stmt),
     154              :                                                visited);
     155        16626 :         if (fn == NULL_TREE)
     156              :           return NULL_TREE;
     157        16271 :         unsigned num_args = gimple_call_num_args (call_stmt);
     158        16271 :         auto_vec<tree> args (num_args);
     159        38046 :         for (unsigned i = 0; i < num_args; i++)
     160              :           {
     161        21775 :             tree arg = gimple_call_arg (call_stmt, i);
     162        21775 :             arg = fixup_tree_for_diagnostic_1 (arg, visited);
     163        21775 :             if (arg == NULL_TREE)
     164            0 :               return NULL_TREE;
     165        21775 :             args.quick_push (arg);
     166              :           }
     167        16271 :         gcc_assert (fn);
     168        16271 :         return build_call_array_loc (gimple_location (call_stmt),
     169              :                                      return_type, fn,
     170        16271 :                                      num_args, args.address ());
     171        16271 :       }
     172              :       break;
     173              :     }
     174              : }
     175              : 
     176              : /* Subroutine of fixup_tree_for_diagnostic: attempt to fixup EXPR,
     177              :    which can be NULL.
     178              :    VISITED must be non-NULL; it is used to ensure termination.  */
     179              : 
     180              : static tree
     181       616265 : fixup_tree_for_diagnostic_1 (tree expr, hash_set<tree> *visited)
     182              : {
     183       616265 :   if (expr
     184       603525 :       && TREE_CODE (expr) == SSA_NAME
     185       923653 :       && (SSA_NAME_VAR (expr) == NULL_TREE
     186       115911 :           || DECL_ARTIFICIAL (SSA_NAME_VAR (expr))))
     187              :     {
     188       192033 :       if (tree var = SSA_NAME_VAR (expr))
     189          556 :         if (VAR_P (var) && DECL_HAS_DEBUG_EXPR_P (var))
     190          137 :           return DECL_DEBUG_EXPR (var);
     191       191896 :       if (tree expr2 = maybe_reconstruct_from_def_stmt (expr, visited))
     192              :         return expr2;
     193              :     }
     194              :   return expr;
     195              : }
     196              : 
     197              : /* We don't want to print '<unknown>' in our diagnostics (PR analyzer/99771),
     198              :    but sometimes we generate diagnostics involving an ssa name for a
     199              :    temporary.
     200              : 
     201              :    Work around this by attempting to reconstruct a tree expression for
     202              :    such temporaries based on their def-stmts.
     203              : 
     204              :    Otherwise return EXPR.
     205              : 
     206              :    EXPR can be NULL.  */
     207              : 
     208              : tree
     209        27861 : fixup_tree_for_diagnostic (tree expr)
     210              : {
     211        27861 :   hash_set<tree> visited;
     212        27861 :   return fixup_tree_for_diagnostic_1 (expr, &visited);
     213        27861 : }
     214              : 
     215              : /* Attempt to generate a tree for the LHS of ASSIGN_STMT.  */
     216              : 
     217              : tree
     218       245394 : get_diagnostic_tree_for_gassign (const gassign *assign_stmt)
     219              : {
     220       245394 :   hash_set<tree> visited;
     221       245394 :   return get_diagnostic_tree_for_gassign_1 (assign_stmt, &visited);
     222       245394 : }
     223              : 
     224              : /* Generate a JSON value for NODE, which can be NULL_TREE.
     225              :    This is intended for debugging the analyzer rather than serialization and
     226              :    thus is a string (or null, for NULL_TREE).  */
     227              : 
     228              : std::unique_ptr<json::value>
     229           44 : tree_to_json (tree node)
     230              : {
     231           44 :   if (!node)
     232            0 :     return std::make_unique<json::literal> (json::JSON_NULL);
     233              : 
     234           44 :   pretty_printer pp;
     235           44 :   dump_generic_node (&pp, node, 0, TDF_VOPS|TDF_MEMSYMS, false);
     236           44 :   return std::make_unique<json::string> (pp_formatted_text (&pp));
     237           44 : }
     238              : 
     239              : /* Generate a JSON value for EVENT_ID.
     240              :    This is intended for debugging the analyzer rather than serialization and
     241              :    thus is a string matching those seen in event messags (or null,
     242              :    for unknown).  */
     243              : 
     244              : std::unique_ptr<json::value>
     245          159 : diagnostic_event_id_to_json (const diagnostics::paths::event_id_t &event_id)
     246              : {
     247          159 :   if (event_id.known_p ())
     248              :     {
     249          159 :       pretty_printer pp;
     250          159 :       pp_printf (&pp, "%@", &event_id);
     251          159 :       return std::make_unique<json::string> (pp_formatted_text (&pp));
     252          159 :     }
     253              :   else
     254            0 :     return std::make_unique<json::literal> (json::JSON_NULL);
     255              : }
     256              : 
     257              : /* Generate a JSON value for OFFSET.
     258              :    This is intended for debugging the analyzer rather than serialization and
     259              :    thus is a string.  */
     260              : 
     261              : std::unique_ptr<json::value>
     262            8 : bit_offset_to_json (const bit_offset_t &offset)
     263              : {
     264            8 :   pretty_printer pp;
     265            8 :   pp_wide_int_large (&pp, offset, SIGNED);
     266            8 :   return std::make_unique<json::string> (pp_formatted_text (&pp));
     267            8 : }
     268              : 
     269              : /* Generate a JSON value for OFFSET.
     270              :    This is intended for debugging the analyzer rather than serialization and
     271              :    thus is a string.  */
     272              : 
     273              : std::unique_ptr<json::value>
     274            8 : byte_offset_to_json (const byte_offset_t &offset)
     275              : {
     276            8 :   pretty_printer pp;
     277            8 :   pp_wide_int_large (&pp, offset, SIGNED);
     278            8 :   return std::make_unique<json::string> (pp_formatted_text (&pp));
     279            8 : }
     280              : 
     281              : /* Workaround for lack of const-correctness of ssa_default_def.  */
     282              : 
     283              : tree
     284        37213 : get_ssa_default_def (const function &fun, tree var)
     285              : {
     286        37213 :   return ssa_default_def (const_cast <function *> (&fun), var);
     287              : }
     288              : 
     289              : } // namespace ana
     290              : 
     291              : /* Helper function for checkers.  Is the CALL to the given function name,
     292              :    and with the given number of arguments?
     293              : 
     294              :    This doesn't resolve function pointers via the region model;
     295              :    is_named_call_p should be used instead, using a fndecl from
     296              :    get_fndecl_for_call; this function should only be used for special cases
     297              :    where it's not practical to get at the region model, or for special
     298              :    analyzer functions such as __analyzer_dump.
     299              : 
     300              :    If LOOK_IN_STD is true, then also look for within std:: for the name.  */
     301              : 
     302              : bool
     303       399542 : is_special_named_call_p (const gcall &call, const char *funcname,
     304              :                          unsigned int num_args, bool look_in_std)
     305              : {
     306       399542 :   gcc_assert (funcname);
     307              : 
     308       399542 :   tree fndecl = gimple_call_fndecl (&call);
     309       399542 :   if (!fndecl)
     310              :     return false;
     311              : 
     312       372033 :   if (is_named_call_p (fndecl, funcname, call, num_args))
     313              :     return true;
     314       370457 :   if (look_in_std)
     315            0 :     if (is_std_named_call_p (fndecl, funcname, call, num_args))
     316              :       return true;
     317              :   return false;
     318              : }
     319              : 
     320              : /* Helper function for checkers.  Is FNDECL an extern fndecl at file scope
     321              :    that has the given FUNCNAME?
     322              : 
     323              :    Compare with special_function_p in calls.cc.  */
     324              : 
     325              : bool
     326      1851333 : is_named_call_p (const_tree fndecl, const char *funcname)
     327              : {
     328      1851333 :   gcc_assert (fndecl);
     329      1851333 :   gcc_assert (funcname);
     330              : 
     331      1851333 :   if (!maybe_special_function_p (fndecl))
     332              :     return false;
     333              : 
     334      1817566 :   tree identifier = DECL_NAME (fndecl);
     335      1817566 :   const char *name = IDENTIFIER_POINTER (identifier);
     336      1817566 :   const char *tname = name;
     337              : 
     338              :   /* Potentially disregard prefix _ or __ in FNDECL's name, but not if
     339              :      FUNCNAME itself has leading underscores (e.g. when looking for
     340              :      "__analyzer_eval").  */
     341      1817566 :   if (funcname[0] != '_' && name[0] == '_')
     342              :     {
     343       734699 :       if (name[1] == '_')
     344       730335 :         tname += 2;
     345              :       else
     346         4364 :         tname += 1;
     347              :     }
     348              : 
     349      1817566 :   return 0 == strcmp (tname, funcname);
     350              : }
     351              : 
     352              : /* Return true if FNDECL is within the namespace "std".
     353              :    Compare with cp/typeck.cc: decl_in_std_namespace_p, but this doesn't
     354              :    rely on being the C++ FE (or handle inline namespaces inside of std).  */
     355              : 
     356              : bool
     357       533786 : is_std_function_p (const_tree fndecl)
     358              : {
     359       533786 :   tree name_decl = DECL_NAME (fndecl);
     360       533786 :   if (!name_decl)
     361              :     return false;
     362       533786 :   if (!DECL_CONTEXT (fndecl))
     363              :     return false;
     364       529075 :   if (TREE_CODE (DECL_CONTEXT (fndecl)) != NAMESPACE_DECL)
     365              :     return false;
     366          481 :   tree ns = DECL_CONTEXT (fndecl);
     367          481 :   if (!(DECL_CONTEXT (ns) == NULL_TREE
     368          476 :         || TREE_CODE (DECL_CONTEXT (ns)) == TRANSLATION_UNIT_DECL))
     369              :     return false;
     370          481 :   if (!DECL_NAME (ns))
     371              :     return false;
     372          481 :   return id_equal ("std", DECL_NAME (ns));
     373              : }
     374              : 
     375              : /* Like is_named_call_p, but look for std::FUNCNAME.  */
     376              : 
     377              : bool
     378       199497 : is_std_named_call_p (const_tree fndecl, const char *funcname)
     379              : {
     380       199497 :   gcc_assert (fndecl);
     381       199497 :   gcc_assert (funcname);
     382              : 
     383       199497 :   if (!is_std_function_p (fndecl))
     384              :     return false;
     385              : 
     386           44 :   tree identifier = DECL_NAME (fndecl);
     387           44 :   const char *name = IDENTIFIER_POINTER (identifier);
     388           44 :   const char *tname = name;
     389              : 
     390              :   /* Don't disregard prefix _ or __ in FNDECL's name.  */
     391              : 
     392           44 :   return 0 == strcmp (tname, funcname);
     393              : }
     394              : 
     395              : /* Helper function for checkers.  Is FNDECL an extern fndecl at file scope
     396              :    that has the given FUNCNAME, and does CALL have the given number of
     397              :    arguments?  */
     398              : 
     399              : bool
     400      1602336 : is_named_call_p (const_tree fndecl, const char *funcname,
     401              :                  const gcall &call, unsigned int num_args)
     402              : {
     403      1602336 :   gcc_assert (fndecl);
     404      1602336 :   gcc_assert (funcname);
     405              : 
     406      1602336 :   if (!is_named_call_p (fndecl, funcname))
     407              :     return false;
     408              : 
     409        19725 :   if (gimple_call_num_args (&call) != num_args)
     410          813 :     return false;
     411              : 
     412              :   return true;
     413              : }
     414              : 
     415              : /* Like is_named_call_p, but check for std::FUNCNAME.  */
     416              : 
     417              : bool
     418       199426 : is_std_named_call_p (const_tree fndecl, const char *funcname,
     419              :                      const gcall &call, unsigned int num_args)
     420              : {
     421       199426 :   gcc_assert (fndecl);
     422       199426 :   gcc_assert (funcname);
     423              : 
     424       199426 :   if (!is_std_named_call_p (fndecl, funcname))
     425              :     return false;
     426              : 
     427           12 :   if (gimple_call_num_args (&call) != num_args)
     428            0 :     return false;
     429              : 
     430              :   return true;
     431              : }
     432              : 
     433              : bool
     434        41757 : is_cxa_throw_p (const gcall &call)
     435              : {
     436        41757 :   tree fndecl = gimple_call_fndecl (&call);
     437        41757 :   if (!fndecl)
     438              :     return false;
     439              : 
     440        38814 :   return is_named_call_p (fndecl, "__cxa_throw");
     441              : }
     442              : 
     443              : bool
     444        41684 : is_cxa_rethrow_p (const gcall &call)
     445              : {
     446        41684 :   tree fndecl = gimple_call_fndecl (&call);
     447        41684 :   if (!fndecl)
     448              :     return false;
     449              : 
     450        38741 :   return is_named_call_p (fndecl, "__cxa_rethrow");
     451              : }
     452              : 
     453              : bool
     454          526 : is_cxa_end_catch_p (const gcall &call)
     455              : {
     456          526 :   tree fndecl = gimple_call_fndecl (&call);
     457          526 :   if (!fndecl)
     458              :     return false;
     459              : 
     460          514 :   return is_named_call_p (fndecl, "__cxa_end_catch");
     461              : }
     462              : 
     463              : /* For a CALL that matched is_special_named_call_p or is_named_call_p for
     464              :    some name, return a name for the called function suitable for use in
     465              :    diagnostics (stripping the leading underscores).  */
     466              : 
     467              : const char *
     468          133 : get_user_facing_name (const gcall &call)
     469              : {
     470          133 :   tree fndecl = gimple_call_fndecl (&call);
     471          133 :   gcc_assert (fndecl);
     472              : 
     473          133 :   tree identifier = DECL_NAME (fndecl);
     474          133 :   gcc_assert (identifier);
     475              : 
     476          133 :   const char *name = IDENTIFIER_POINTER (identifier);
     477              : 
     478              :   /* Strip prefix _ or __ in FNDECL's name.  */
     479          133 :   if (name[0] == '_')
     480              :     {
     481            0 :       if (name[1] == '_')
     482            0 :         return name + 2;
     483              :       else
     484            0 :         return name + 1;
     485              :     }
     486              : 
     487              :   return name;
     488              : }
     489              : 
     490              : /* Generate a label_text instance by formatting FMT, using a
     491              :    temporary clone of the global_dc's printer (thus using its
     492              :    formatting callbacks).
     493              : 
     494              :    Colorize if the global_dc supports colorization and CAN_COLORIZE is
     495              :    true.  */
     496              : 
     497              : label_text
     498         4114 : make_label_text (bool can_colorize, const char *fmt, ...)
     499              : {
     500         4114 :   std::unique_ptr<pretty_printer> pp (global_dc->clone_printer ());
     501         4114 :   pp_clear_output_area (pp.get ());
     502              : 
     503         4114 :   if (!can_colorize)
     504         4111 :     pp_show_color (pp.get ()) = false;
     505              : 
     506         4114 :   rich_location rich_loc (line_table, UNKNOWN_LOCATION);
     507              : 
     508         4114 :   va_list ap;
     509              : 
     510         4114 :   va_start (ap, fmt);
     511              : 
     512         4114 :   text_info ti (_(fmt), &ap, 0, nullptr, &rich_loc);
     513         4114 :   pp_format (pp.get (), &ti);
     514         4114 :   pp_output_formatted_text (pp.get ());
     515              : 
     516         4114 :   va_end (ap);
     517              : 
     518         4114 :   label_text result = label_text::take (xstrdup (pp_formatted_text (pp.get ())));
     519         8228 :   return result;
     520         4114 : }
     521              : 
     522              : /* As above, but with singular vs plural.  */
     523              : 
     524              : label_text
     525            0 : make_label_text_n (bool can_colorize, unsigned HOST_WIDE_INT n,
     526              :                    const char *singular_fmt,
     527              :                    const char *plural_fmt, ...)
     528              : {
     529            0 :   std::unique_ptr<pretty_printer> pp (global_dc->clone_printer ());
     530            0 :   pp_clear_output_area (pp.get ());
     531              : 
     532            0 :   if (!can_colorize)
     533            0 :     pp_show_color (pp.get ()) = false;
     534              : 
     535            0 :   rich_location rich_loc (line_table, UNKNOWN_LOCATION);
     536              : 
     537            0 :   va_list ap;
     538              : 
     539            0 :   va_start (ap, plural_fmt);
     540              : 
     541            0 :   const char *fmt = ngettext (singular_fmt, plural_fmt, n);
     542              : 
     543            0 :   text_info ti (fmt, &ap, 0, nullptr, &rich_loc);
     544              : 
     545            0 :   pp_format (pp.get (), &ti);
     546            0 :   pp_output_formatted_text (pp.get ());
     547              : 
     548            0 :   va_end (ap);
     549              : 
     550            0 :   label_text result
     551            0 :     = label_text::take (xstrdup (pp_formatted_text (pp.get ())));
     552            0 :   return result;
     553            0 : }
     554              : 
     555              : #endif /* #if ENABLE_ANALYZER */
        

Generated by: LCOV version 2.4-beta

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.