]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_string_literals.c
Fix ICE in get_substring_ranges_for_loc on __FILE__ (PR c++/87721)
[thirdparty/gcc.git] / 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-fold.h"
21 #include "tree-eh.h"
22 #include "gimple-expr.h"
23 #include "is-a.h"
24 #include "gimple.h"
25 #include "gimple-iterator.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 pass_info.pass = new pass_test_string_literals (g);
212 pass_info.reference_pass_name = "ssa";
213 pass_info.ref_pass_instance_number = 1;
214 pass_info.pos_op = PASS_POS_INSERT_AFTER;
215 register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
216 &pass_info);
217
218 return 0;
219 }