1 /* { dg-options "-O" } */
2 
3 #include "gcc-plugin.h"
4 #include "config.h"
5 #include "system.h"
6 #include "coretypes.h"
7 #include "tm.h"
8 #include "tree.h"
9 #include "stringpool.h"
10 #include "toplev.h"
11 #include "basic-block.h"
12 #include "hash-table.h"
13 #include "vec.h"
14 #include "ggc.h"
15 #include "basic-block.h"
16 #include "tree-ssa-alias.h"
17 #include "internal-fn.h"
18 #include "gimple-fold.h"
19 #include "tree-eh.h"
20 #include "gimple-expr.h"
21 #include "is-a.h"
22 #include "gimple.h"
23 #include "gimple-iterator.h"
24 #include "tree.h"
25 #include "tree-pass.h"
26 #include "intl.h"
27 #include "plugin-version.h"
28 #include "c-family/c-common.h"
29 #include "diagnostic.h"
30 #include "context.h"
31 
32 int plugin_is_GPL_compatible;
33 
34 /* A custom pass for emitting dummy warnings from the middle-end.  */
35 
36 const pass_data pass_data_test_groups =
37 {
38   GIMPLE_PASS, /* type */
39   "test_groups", /* 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_test_groups : public gimple_opt_pass
50 {
51 public:
pass_test_groups(gcc::context * ctxt)52   pass_test_groups(gcc::context *ctxt)
53     : gimple_opt_pass(pass_data_test_groups, ctxt)
54   {}
55 
56   /* opt_pass methods: */
gate(function *)57   bool gate (function *) { return true; }
58   virtual unsigned int execute (function *);
59 
60 }; // class pass_test_groups
61 
62 /* Determine if STMT is a call with NUM_ARGS arguments to a function
63    named FUNCNAME.
64    If so, return STMT as a gcall *.  Otherwise return NULL.  */
65 
66 static gcall *
check_for_named_call(gimple * stmt,const char * funcname,unsigned int num_args)67 check_for_named_call (gimple *stmt,
68 		      const char *funcname, unsigned int num_args)
69 {
70   gcc_assert (funcname);
71 
72   gcall *call = dyn_cast <gcall *> (stmt);
73   if (!call)
74     return NULL;
75 
76   tree fndecl = gimple_call_fndecl (call);
77   if (!fndecl)
78     return NULL;
79 
80   if (strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), funcname))
81     return NULL;
82 
83   if (gimple_call_num_args (call) != num_args)
84     {
85       error_at (stmt->location, "expected number of args: %i (got %i)",
86 		num_args, gimple_call_num_args (call));
87       return NULL;
88     }
89 
90   return call;
91 }
92 
93 /* Emit a warning at LOC.  */
94 
95 static void
emit_warning(location_t loc)96 emit_warning (location_t loc)
97 {
98   source_range src_range = get_range_from_loc (line_table, loc);
99   warning_at (loc, 0, "range %i:%i-%i:%i",
100 	      LOCATION_LINE (src_range.m_start),
101 	      LOCATION_COLUMN (src_range.m_start),
102 	      LOCATION_LINE (src_range.m_finish),
103 	      LOCATION_COLUMN (src_range.m_finish));
104 }
105 
106 /* Code for simulating the emission of a warning from the middle-end.
107    Emit a warning for each call to a function named "__emit_warning".  */
108 
109 static void
test_groups(gimple * stmt)110 test_groups (gimple *stmt)
111 {
112   gcall *call = check_for_named_call (stmt, "__emit_warning", 1);
113   if (!call)
114     return;
115 
116   /* We expect an ADDR_EXPR with a STRING_CST inside it for the
117      initial arg.  */
118   tree t_addr_string = gimple_call_arg (call, 0);
119   if (TREE_CODE (t_addr_string) != ADDR_EXPR)
120     {
121       error_at (call->location, "string literal required for arg 1");
122       return;
123     }
124 
125   tree t_string = TREE_OPERAND (t_addr_string, 0);
126   if (TREE_CODE (t_string) != STRING_CST)
127     {
128       error_at (call->location, "string literal required for arg 1");
129       return;
130     }
131 
132   {
133     auto_diagnostic_group d;
134     if (warning_at (call->location, 0, "%s", call,
135 		    TREE_STRING_POINTER (t_string)))
136       {
137 	inform (call->location, "message for note");
138 	inform (call->location, " some more detail");
139 	inform (call->location, "  yet more detail");
140       }
141   }
142   inform (call->location, "an unrelated message");
143 }
144 
145 /* Call test_groups on every statement within FUN.  */
146 
147 unsigned int
execute(function * fun)148 pass_test_groups::execute (function *fun)
149 {
150   gimple_stmt_iterator gsi;
151   basic_block bb;
152 
153   FOR_EACH_BB_FN (bb, fun)
154     for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
155       {
156 	gimple *stmt = gsi_stmt (gsi);
157 	test_groups (stmt);
158       }
159 
160   return 0;
161 }
162 
163 /* Custom diagnostic callback, to avoid having the path in the
164    expected output.  */
165 
166 void
test_diagnostic_starter(diagnostic_context * context,diagnostic_info * diagnostic)167 test_diagnostic_starter (diagnostic_context *context,
168 			 diagnostic_info *diagnostic)
169 {
170   pp_set_prefix (context->printer, xstrdup ("PREFIX: "));
171 }
172 
173 /* Custom diagnostic callback, to avoid having the path in the
174    expected output.  */
175 
176 void
test_diagnostic_start_span_fn(diagnostic_context * context,expanded_location exploc)177 test_diagnostic_start_span_fn (diagnostic_context *context,
178 			       expanded_location exploc)
179 {
180   pp_string (context->printer, "START_SPAN_FN: ");
181   pp_newline (context->printer);
182 }
183 
184 /* Custom diagnostic callback: loudly announce a new diagnostic group.  */
185 
186 static void
test_begin_group_cb(diagnostic_context * context)187 test_begin_group_cb (diagnostic_context * context)
188 {
189   pp_string (context->printer,
190 	     "================================= BEGIN GROUP ==============================");
191   pp_newline (context->printer);
192 }
193 
194 /* Custom diagnostic callback: loudly announce the end of a
195    diagnostic group.  */
196 
197 static void
test_end_group_cb(diagnostic_context * context)198 test_end_group_cb (diagnostic_context * context)
199 {
200   pp_set_prefix (context->printer, NULL);
201   pp_string (context->printer,
202 	     "---------------------------------- END GROUP -------------------------------");
203   pp_newline_and_flush (context->printer);
204 }
205 
206 /* Entrypoint for the plugin.
207    Install custom callbacks into the global_dc.
208    Create and register the custom pass.  */
209 
210 int
plugin_init(struct plugin_name_args * plugin_info,struct plugin_gcc_version * version)211 plugin_init (struct plugin_name_args *plugin_info,
212 	     struct plugin_gcc_version *version)
213 {
214   struct register_pass_info pass_info;
215   const char *plugin_name = plugin_info->base_name;
216   int argc = plugin_info->argc;
217   struct plugin_argument *argv = plugin_info->argv;
218 
219   if (!plugin_default_version_check (version, &gcc_version))
220     return 1;
221 
222   diagnostic_starter (global_dc) = test_diagnostic_starter;
223   global_dc->start_span = test_diagnostic_start_span_fn;
224   global_dc->begin_group_cb = test_begin_group_cb;
225   global_dc->end_group_cb = test_end_group_cb;
226 
227   pass_info.pass = new pass_test_groups (g);
228   pass_info.reference_pass_name = "*warn_function_noreturn";
229   pass_info.ref_pass_instance_number = 1;
230   pass_info.pos_op = PASS_POS_INSERT_AFTER;
231   register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
232 		     &pass_info);
233 
234   return 0;
235 }
236