LCOV - code coverage report
Current view: top level - gcc - gimple-warn-recursion.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 98.4 % 64 63
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 5 5
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* -Winfinite-recursion support.
       2              :    Copyright (C) 2021-2026 Free Software Foundation, Inc.
       3              :    Contributed by Martin Sebor <msebor@redhat.com>
       4              : 
       5              :    This file is part of GCC.
       6              : 
       7              :    GCC is free software; you can redistribute it and/or modify
       8              :    it 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,
      13              :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14              :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15              :    GNU 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 "config.h"
      22              : #include "system.h"
      23              : #include "coretypes.h"
      24              : #include "backend.h"
      25              : #include "tree.h"
      26              : #include "gimple.h"
      27              : #include "tree-pass.h"
      28              : #include "ssa.h"
      29              : #include "diagnostic-core.h"
      30              : // #include "tree-dfa.h"
      31              : #include "attribs.h"
      32              : #include "gimple-iterator.h"
      33              : 
      34              : namespace {
      35              : 
      36              : const pass_data warn_recursion_data =
      37              : {
      38              :   GIMPLE_PASS, /* type */
      39              :   "*infinite-recursion", /* name */
      40              :   OPTGROUP_NONE, /* optinfo_flags */
      41              :   TV_NONE, /* tv_id */
      42              :   PROP_ssa, /* properties_required */
      43              :   0, /* properties_provided */
      44              :   0, /* properties_destroyed */
      45              :   0, /* todo_flags_start */
      46              :   0, /* todo_flags_finish */
      47              : };
      48              : 
      49              : class pass_warn_recursion : public gimple_opt_pass
      50              : {
      51              : public:
      52              :   pass_warn_recursion (gcc::context *);
      53              : 
      54              : private:
      55      2848482 :   bool gate (function *) final override { return warn_infinite_recursion; }
      56              : 
      57              :   unsigned int execute (function *) final override;
      58              : 
      59              :   bool find_function_exit (basic_block);
      60              : 
      61              :   /* Recursive calls found in M_FUNC.  */
      62              :   vec<gimple *> *m_calls;
      63              :   /* Basic blocks already visited in the current function.  */
      64              :   bitmap m_visited;
      65              :   /* The current function.  */
      66              :   function *m_func;
      67              :   /* The current function code if it's (also) a built-in.  */
      68              :   built_in_function m_built_in;
      69              :   /* True if M_FUNC is a noreturn function.  */
      70              :   bool noreturn_p;
      71              : };
      72              : 
      73              : /* Initialize the pass and its members.  */
      74              : 
      75       285722 : pass_warn_recursion::pass_warn_recursion (gcc::context *ctxt)
      76              :   : gimple_opt_pass (warn_recursion_data, ctxt),
      77       285722 :     m_calls (), m_visited (), m_func (), m_built_in (), noreturn_p ()
      78              : {
      79       285722 : }
      80              : 
      81              : /* Return true if there is path from BB to M_FUNC exit point along which
      82              :    there is no (recursive) call to M_FUNC.  */
      83              : 
      84              : bool
      85      1282457 : pass_warn_recursion::find_function_exit (basic_block bb)
      86              : {
      87      1282457 :   if (!bitmap_set_bit (m_visited, bb->index))
      88              :     return false;
      89              : 
      90      1243914 :   if (bb == EXIT_BLOCK_PTR_FOR_FN (m_func))
      91              :     return true;
      92              : 
      93              :   /* Iterate over statements in BB, looking for a call to FNDECL.  */
      94      6756307 :   for (auto si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next_nondebug (&si))
      95              :     {
      96      4760502 :       gimple *stmt = gsi_stmt (si);
      97      4760502 :       if (!is_gimple_call (stmt))
      98      4759493 :         continue;
      99              : 
     100       660369 :       if (gimple_call_builtin_p (stmt, BUILT_IN_LONGJMP))
     101              :         /* A longjmp breaks infinite recursion.  */
     102         1009 :         return true;
     103              : 
     104       660366 :       if (tree fndecl = gimple_call_fndecl (stmt))
     105              :         {
     106              :           /* A throw statement breaks infinite recursion.  */
     107       603557 :           tree id = DECL_NAME (fndecl);
     108       603557 :           const char *name = IDENTIFIER_POINTER (id);
     109       603557 :           if (startswith (name, "__cxa_throw"))
     110              :             return true;
     111              :           /* As does a call to POSIX siglongjmp.  */
     112       603056 :           if (!strcmp (name, "siglongjmp"))
     113              :             return true;
     114              : 
     115       603053 :           if (m_built_in
     116          479 :               && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
     117       603455 :               && m_built_in == DECL_FUNCTION_CODE (fndecl))
     118              :             {
     119            4 :               const char *cname
     120            4 :                 = IDENTIFIER_POINTER (DECL_NAME (current_function_decl));
     121              :               /* Don't warn about gnu_inline extern inline function
     122              :                  like strcpy calling __builtin_strcpy, that is fine,
     123              :                  if some call is made (the builtin isn't expanded inline),
     124              :                  a call is made to the external definition.  */
     125            4 :               if (!(DECL_DECLARED_INLINE_P (current_function_decl)
     126            2 :                     && DECL_EXTERNAL (current_function_decl))
     127            6 :                   || strcmp (name, cname) == 0)
     128              :                 {
     129              :                   /* The call is being made from the definition of a built-in
     130              :                      (e.g., in a replacement of one) to itself.  */
     131            3 :                   m_calls->safe_push (stmt);
     132            3 :                   return false;
     133              :                 }
     134              :             }
     135              :         }
     136              : 
     137       659859 :       if (noreturn_p)
     138              :         {
     139              :           /* A noreturn call breaks infinite recursion.  */
     140         1299 :           int flags = gimple_call_flags (stmt);
     141         1299 :           if (flags & ECF_NORETURN)
     142              :             return true;
     143              :         }
     144              : 
     145       659548 :       tree callee = gimple_call_fndecl (stmt);
     146       659548 :       if (!callee || m_func->decl != callee)
     147       659360 :         continue;
     148              : 
     149              :       /* Add the recursive call to the vector and return false.  */
     150          188 :       m_calls->safe_push (stmt);
     151          188 :       return false;
     152              :     }
     153              : 
     154              :   /* If no call to FNDECL has been found search all BB's successors.  */
     155       997398 :   edge e;
     156       997398 :   edge_iterator ei;
     157      1163531 :   FOR_EACH_EDGE (e, ei, bb->succs)
     158      1034504 :     if (find_function_exit (e->dest))
     159              :       return true;
     160              : 
     161              :   return false;
     162              : }
     163              : 
     164              : 
     165              : /* Search FUNC for unconditionally infinitely recursive calls to self
     166              :    and issue a warning if it is such a function.  */
     167              : 
     168              : unsigned int
     169       247953 : pass_warn_recursion::execute (function *func)
     170              : {
     171       247953 :   auto_bitmap visited;
     172       247953 :   auto_vec<gimple *> calls;
     173              : 
     174       247953 :   m_visited = visited;
     175       247953 :   m_calls = &calls;
     176       247953 :   m_func = func;
     177              : 
     178              :   /* Avoid diagnosing an apparently infinitely recursive function that
     179              :      doesn't return where the infinite recursion might be avoided by
     180              :      a call to another noreturn function.  */
     181       247953 :   noreturn_p = lookup_attribute ("noreturn", DECL_ATTRIBUTES (m_func->decl));
     182              : 
     183       247953 :   if (fndecl_built_in_p (m_func->decl, BUILT_IN_NORMAL))
     184          142 :     m_built_in = DECL_FUNCTION_CODE (m_func->decl);
     185              :   else
     186       247811 :     m_built_in = BUILT_IN_NONE;
     187              : 
     188       247953 :   basic_block entry_bb = ENTRY_BLOCK_PTR_FOR_FN (func);
     189              : 
     190       248010 :   if (find_function_exit (entry_bb) || m_calls->length () == 0)
     191              :     return 0;
     192              : 
     193           57 :   auto_diagnostic_group d;
     194           57 :   if (warning_at (DECL_SOURCE_LOCATION (func->decl),
     195           57 :                   OPT_Winfinite_recursion,
     196              :                   "infinite recursion detected"))
     197          240 :     for (auto stmt: *m_calls)
     198              :       {
     199           69 :         location_t loc = gimple_location (stmt);
     200           69 :         if (loc == UNKNOWN_LOCATION)
     201            0 :           continue;
     202              : 
     203           69 :         inform (loc, "recursive call");
     204              :       }
     205              : 
     206           57 :   return 0;
     207       248010 : }
     208              : 
     209              : } // namespace
     210              : 
     211              : gimple_opt_pass *
     212       285722 : make_pass_warn_recursion (gcc::context *ctxt)
     213              : {
     214       285722 :   return new pass_warn_recursion (ctxt);
     215              : }
        

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.