(root)/
gcc-13.2.0/
gcc/
testsuite/
gcc.dg/
plugin/
diagnostic_plugin_test_string_literals.c
       1  /* This plugin uses the diagnostics code to verify tracking of source code
       2     locations within string literals.  */
       3  /* { dg-options "-O" } */
       4  
       5  #include "gcc-plugin.h"
       6  #include "config.h"
       7  #include "system.h"
       8  #include "coretypes.h"
       9  #include "tm.h"
      10  #include "tree.h"
      11  #include "stringpool.h"
      12  #include "toplev.h"
      13  #include "basic-block.h"
      14  #include "hash-table.h"
      15  #include "vec.h"
      16  #include "ggc.h"
      17  #include "basic-block.h"
      18  #include "tree-ssa-alias.h"
      19  #include "internal-fn.h"
      20  #include "gimple.h"
      21  #include "gimple-iterator.h"
      22  #include "gimple-fold.h"
      23  #include "tree-eh.h"
      24  #include "gimple-expr.h"
      25  #include "is-a.h"
      26  #include "tree.h"
      27  #include "tree-pass.h"
      28  #include "intl.h"
      29  #include "plugin-version.h"
      30  #include "c-family/c-common.h"
      31  #include "diagnostic.h"
      32  #include "context.h"
      33  #include "print-tree.h"
      34  #include "cpplib.h"
      35  #include "c-family/c-pragma.h"
      36  #include "substring-locations.h"
      37  
      38  int plugin_is_GPL_compatible;
      39  
      40  /* A custom pass for printing string literal location information.  */
      41  
      42  const pass_data pass_data_test_string_literals =
      43  {
      44    GIMPLE_PASS, /* type */
      45    "test_string_literals", /* name */
      46    OPTGROUP_NONE, /* optinfo_flags */
      47    TV_NONE, /* tv_id */
      48    PROP_ssa, /* properties_required */
      49    0, /* properties_provided */
      50    0, /* properties_destroyed */
      51    0, /* todo_flags_start */
      52    0, /* todo_flags_finish */
      53  };
      54  
      55  class pass_test_string_literals : public gimple_opt_pass
      56  {
      57  public:
      58    pass_test_string_literals(gcc::context *ctxt)
      59      : gimple_opt_pass(pass_data_test_string_literals, ctxt)
      60    {}
      61  
      62    /* opt_pass methods: */
      63    bool gate (function *) { return true; }
      64    virtual unsigned int execute (function *);
      65  
      66  }; // class pass_test_string_literals
      67  
      68  /* Determine if STMT is a call with NUM_ARGS arguments to a function
      69     named FUNCNAME.
      70     If so, return STMT as a gcall *.  Otherwise return NULL.  */
      71  
      72  static gcall *
      73  check_for_named_call (gimple *stmt,
      74  		      const char *funcname, unsigned int num_args)
      75  {
      76    gcc_assert (funcname);
      77  
      78    gcall *call = dyn_cast <gcall *> (stmt);
      79    if (!call)
      80      return NULL;
      81  
      82    tree fndecl = gimple_call_fndecl (call);
      83    if (!fndecl)
      84      return NULL;
      85  
      86    if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname))
      87      return NULL;
      88  
      89    if (gimple_call_num_args (call) != num_args)
      90      {
      91        error_at (stmt->location, "expected number of args: %i (got %i)",
      92  		num_args, gimple_call_num_args (call));
      93        return NULL;
      94      }
      95  
      96    return call;
      97  }
      98  
      99  /* Emit a warning at LOC.  */
     100  
     101  static void
     102  emit_warning (location_t loc)
     103  {
     104    source_range src_range = get_range_from_loc (line_table, loc);
     105    warning_at (loc, 0, "range %i:%i-%i:%i",
     106  	      LOCATION_LINE (src_range.m_start),
     107  	      LOCATION_COLUMN (src_range.m_start),
     108  	      LOCATION_LINE (src_range.m_finish),
     109  	      LOCATION_COLUMN (src_range.m_finish));
     110  }
     111  
     112  /* Support code for verifying that we are correctly tracking ranges
     113     within string literals, for use by diagnostic-test-string-literals-*.c.
     114     Emit a warning showing the range of a string literal, for each call to
     115     a function named "__emit_string_literal_range".
     116     The initial argument should be a string literal; arguments 2, 3, and 4
     117     should be integer constants, giving the caret and range within the string
     118     to be printed.  */
     119  
     120  static void
     121  test_string_literals (gimple *stmt)
     122  {
     123    gcall *call = check_for_named_call (stmt, "__emit_string_literal_range", 4);
     124    if (!call)
     125      return;
     126  
     127    /* We expect an ADDR_EXPR with a STRING_CST inside it for the
     128       initial arg.  */
     129    tree t_addr_string = gimple_call_arg (call, 0);
     130    if (TREE_CODE (t_addr_string) != ADDR_EXPR)
     131      {
     132        error_at (call->location, "string literal required for arg 1");
     133        return;
     134      }
     135  
     136    tree t_string = TREE_OPERAND (t_addr_string, 0);
     137    if (TREE_CODE (t_string) != STRING_CST)
     138      {
     139        error_at (call->location, "string literal required for arg 1");
     140        return;
     141      }
     142  
     143    tree t_caret_idx = fold (gimple_call_arg (call, 1));
     144    if (TREE_CODE (t_caret_idx) != INTEGER_CST)
     145      {
     146        error_at (call->location, "integer constant required for arg 2");
     147        return;
     148      }
     149    int caret_idx = TREE_INT_CST_LOW (t_caret_idx);
     150  
     151    tree t_start_idx = fold (gimple_call_arg (call, 2));
     152    if (TREE_CODE (t_start_idx) != INTEGER_CST)
     153      {
     154        error_at (call->location, "integer constant required for arg 3");
     155        return;
     156      }
     157    int start_idx = TREE_INT_CST_LOW (t_start_idx);
     158  
     159    tree t_end_idx = fold (gimple_call_arg (call, 3));
     160    if (TREE_CODE (t_end_idx) != INTEGER_CST)
     161      {
     162        error_at (call->location, "integer constant required for arg 4");
     163        return;
     164      }
     165    int end_idx = TREE_INT_CST_LOW (t_end_idx);
     166  
     167    /* A STRING_CST doesn't have a location, but the ADDR_EXPR does.  */
     168    location_t strloc = EXPR_LOCATION (t_addr_string);
     169    location_t loc;
     170    substring_loc substr_loc (strloc, TREE_TYPE (t_string),
     171  			    caret_idx, start_idx, end_idx);
     172    const char *err = substr_loc.get_location (&loc);
     173    if (err)
     174      error_at (strloc, "unable to read substring location: %s", err);
     175    else
     176      emit_warning (loc);
     177  }
     178  
     179  /* Call test_string_literals on every statement within FUN.  */
     180  
     181  unsigned int
     182  pass_test_string_literals::execute (function *fun)
     183  {
     184    gimple_stmt_iterator gsi;
     185    basic_block bb;
     186  
     187    FOR_EACH_BB_FN (bb, fun)
     188      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
     189        {
     190  	gimple *stmt = gsi_stmt (gsi);
     191  	test_string_literals (stmt);
     192        }
     193  
     194    return 0;
     195  }
     196  
     197  /* Entrypoint for the plugin.  Create and register the custom pass.  */
     198  
     199  int
     200  plugin_init (struct plugin_name_args *plugin_info,
     201  	     struct plugin_gcc_version *version)
     202  {
     203    struct register_pass_info pass_info;
     204    const char *plugin_name = plugin_info->base_name;
     205    int argc = plugin_info->argc;
     206    struct plugin_argument *argv = plugin_info->argv;
     207  
     208    if (!plugin_default_version_check (version, &gcc_version))
     209      return 1;
     210  
     211    global_dc->caret_max_width = 80;
     212  
     213    pass_info.pass = new pass_test_string_literals (g);
     214    pass_info.reference_pass_name = "ssa";
     215    pass_info.ref_pass_instance_number = 1;
     216    pass_info.pos_op = PASS_POS_INSERT_AFTER;
     217    register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
     218  		     &pass_info);
     219  
     220    return 0;
     221  }