1 | /* Implementation of gcc_rich_location class |
2 | Copyright (C) 2014-2023 Free Software Foundation, Inc. |
3 | |
4 | This file is part of GCC. |
5 | |
6 | GCC is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free |
8 | Software Foundation; either version 3, or (at your option) any later |
9 | version. |
10 | |
11 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | for more details. |
15 | |
16 | You should have received a copy of the GNU General Public License |
17 | along with GCC; see the file COPYING3. If not see |
18 | <http://www.gnu.org/licenses/>. */ |
19 | |
20 | #include "config.h" |
21 | #include "system.h" |
22 | #include "coretypes.h" |
23 | #include "tm.h" |
24 | #include "rtl.h" |
25 | #include "hash-set.h" |
26 | #include "vec.h" |
27 | #include "input.h" |
28 | #include "alias.h" |
29 | #include "symtab.h" |
30 | #include "inchash.h" |
31 | #include "tree-core.h" |
32 | #include "tree.h" |
33 | #include "diagnostic-core.h" |
34 | #include "gcc-rich-location.h" |
35 | #include "print-tree.h" |
36 | #include "pretty-print.h" |
37 | #include "intl.h" |
38 | #include "cpplib.h" |
39 | #include "diagnostic.h" |
40 | |
41 | /* Add a range to the rich_location, covering expression EXPR, |
42 | using LABEL if non-NULL. */ |
43 | |
44 | void |
45 | gcc_rich_location::add_expr (tree expr, range_label *label) |
46 | { |
47 | gcc_assert (expr); |
48 | |
49 | if (CAN_HAVE_RANGE_P (expr)) |
50 | add_range (EXPR_LOCATION (expr), range_display_kind: SHOW_RANGE_WITHOUT_CARET, label); |
51 | } |
52 | |
53 | /* If T is an expression, add a range for it to the rich_location, |
54 | using LABEL if non-NULL. */ |
55 | |
56 | void |
57 | gcc_rich_location::maybe_add_expr (tree t, range_label *label) |
58 | { |
59 | if (EXPR_P (t)) |
60 | add_expr (expr: t, label); |
61 | } |
62 | |
63 | /* Add a fixit hint suggesting replacing the range at MISSPELLED_TOKEN_LOC |
64 | with the identifier HINT_ID. */ |
65 | |
66 | void |
67 | gcc_rich_location::add_fixit_misspelled_id (location_t misspelled_token_loc, |
68 | tree hint_id) |
69 | { |
70 | gcc_assert (TREE_CODE (hint_id) == IDENTIFIER_NODE); |
71 | |
72 | add_fixit_replace (where: misspelled_token_loc, IDENTIFIER_POINTER (hint_id)); |
73 | } |
74 | |
75 | /* Return true if there is nothing on LOC's line before LOC. */ |
76 | |
77 | static bool |
78 | blank_line_before_p (location_t loc) |
79 | { |
80 | expanded_location exploc = expand_location (loc); |
81 | char_span line = location_get_source_line (file_path: exploc.file, line: exploc.line); |
82 | if (!line) |
83 | return false; |
84 | if (line.length () < (size_t)exploc.column) |
85 | return false; |
86 | /* Columns are 1-based. */ |
87 | for (int column = 1; column < exploc.column; ++column) |
88 | if (!ISSPACE (line[column - 1])) |
89 | return false; |
90 | return true; |
91 | } |
92 | |
93 | /* Subroutine of gcc_rich_location::add_fixit_insert_formatted. |
94 | Return true if we should add the content on its own line, |
95 | false otherwise. |
96 | If true is returned then *OUT_START_OF_LINE is written to. */ |
97 | |
98 | static bool |
99 | use_new_line (location_t insertion_point, location_t indent, |
100 | location_t *out_start_of_line) |
101 | { |
102 | if (indent == UNKNOWN_LOCATION) |
103 | return false; |
104 | const line_map *indent_map = linemap_lookup (line_table, indent); |
105 | if (linemap_macro_expansion_map_p (indent_map)) |
106 | return false; |
107 | |
108 | if (!blank_line_before_p (loc: insertion_point)) |
109 | return false; |
110 | |
111 | /* Locate the start of the line containing INSERTION_POINT. */ |
112 | const line_map *insertion_point_map |
113 | = linemap_lookup (line_table, insertion_point); |
114 | if (linemap_macro_expansion_map_p (insertion_point_map)) |
115 | return false; |
116 | const line_map_ordinary *ordmap |
117 | = linemap_check_ordinary (map: insertion_point_map); |
118 | expanded_location exploc_insertion_point = expand_location (insertion_point); |
119 | location_t start_of_line |
120 | = linemap_position_for_line_and_column (set: line_table, ordmap, |
121 | exploc_insertion_point.line, 1); |
122 | *out_start_of_line = start_of_line; |
123 | return true; |
124 | } |
125 | |
126 | /* Add a fix-it hint suggesting the insertion of CONTENT before |
127 | INSERTION_POINT. |
128 | |
129 | Attempt to handle formatting: if INSERTION_POINT is the first thing on |
130 | its line, and INDENT is sufficiently sane, then add CONTENT on its own |
131 | line, using the indentation of INDENT. |
132 | Otherwise, add CONTENT directly before INSERTION_POINT. |
133 | |
134 | For example, adding "CONTENT;" with the closing brace as the insertion |
135 | point and "INDENT;" as the indentation point: |
136 | |
137 | if () |
138 | { |
139 | INDENT; |
140 | } |
141 | |
142 | would lead to: |
143 | |
144 | if () |
145 | { |
146 | INDENT; |
147 | CONTENT; |
148 | } |
149 | |
150 | but adding it to: |
151 | |
152 | if () {INDENT;} |
153 | |
154 | would lead to: |
155 | |
156 | if () {INDENT;CONTENT;} |
157 | */ |
158 | |
159 | void |
160 | gcc_rich_location::add_fixit_insert_formatted (const char *content, |
161 | location_t insertion_point, |
162 | location_t indent) |
163 | { |
164 | location_t start_of_line; |
165 | if (use_new_line (insertion_point, indent, out_start_of_line: &start_of_line)) |
166 | { |
167 | /* Add CONTENT on its own line, using the indentation of INDENT. */ |
168 | |
169 | /* Generate an insertion string, indenting by the amount INDENT |
170 | was indented. */ |
171 | int indent_column = LOCATION_COLUMN (get_start (indent)); |
172 | pretty_printer tmp_pp; |
173 | pretty_printer *pp = &tmp_pp; |
174 | /* Columns are 1-based. */ |
175 | for (int column = 1; column < indent_column; ++column) |
176 | pp_space (pp); |
177 | pp_string (pp, content); |
178 | pp_newline (pp); |
179 | |
180 | add_fixit_insert_before (where: start_of_line, new_content: pp_formatted_text (pp)); |
181 | } |
182 | else |
183 | add_fixit_insert_before (where: insertion_point, new_content: content); |
184 | } |
185 | |
186 | /* Implementation of range_label::get_text for |
187 | maybe_range_label_for_tree_type_mismatch. |
188 | |
189 | If both expressions are non-NULL, then generate text describing |
190 | the first expression's type (using the other expression's type |
191 | for comparison, analogous to %H and %I in the C++ frontend, but |
192 | on expressions rather than types). */ |
193 | |
194 | label_text |
195 | maybe_range_label_for_tree_type_mismatch::get_text (unsigned range_idx) const |
196 | { |
197 | if (m_expr == NULL_TREE |
198 | || !EXPR_P (m_expr)) |
199 | return label_text::borrow (NULL); |
200 | tree expr_type = TREE_TYPE (m_expr); |
201 | |
202 | tree other_type = NULL_TREE; |
203 | if (CAN_HAVE_LOCATION_P (m_other_expr)) |
204 | other_type = TREE_TYPE (m_other_expr); |
205 | |
206 | range_label_for_type_mismatch inner (expr_type, other_type); |
207 | return inner.get_text (range_idx); |
208 | } |
209 | |
210 | /* binary_op_rich_location's ctor. |
211 | |
212 | If use_operator_loc_p (LOC, ARG0, ARG1), then attempt to make a 3-location |
213 | rich_location of the form: |
214 | |
215 | arg_0 op arg_1 |
216 | ~~~~~ ^~ ~~~~~ |
217 | | | |
218 | | arg1 type |
219 | arg0 type |
220 | |
221 | labelling the types of the arguments if SHOW_TYPES is true. |
222 | |
223 | Otherwise, make a 1-location rich_location using the compound |
224 | location within LOC: |
225 | |
226 | arg_0 op arg_1 |
227 | ~~~~~~^~~~~~~~ |
228 | |
229 | for which we can't label the types. */ |
230 | |
231 | binary_op_rich_location::binary_op_rich_location (const op_location_t &loc, |
232 | tree arg0, tree arg1, |
233 | bool show_types) |
234 | : gcc_rich_location (loc.m_combined_loc), |
235 | m_label_for_arg0 (arg0, arg1), |
236 | m_label_for_arg1 (arg1, arg0) |
237 | { |
238 | /* Default (above) to using the combined loc. |
239 | Potentially override it here: if we have location information for the |
240 | operator and for both arguments, then split them all out. |
241 | Alternatively, override it if we don't have the combined location. */ |
242 | if (use_operator_loc_p (loc, arg0, arg1)) |
243 | { |
244 | set_range (idx: 0, loc: loc.m_operator_loc, range_display_kind: SHOW_RANGE_WITH_CARET); |
245 | maybe_add_expr (t: arg0, label: show_types ? &m_label_for_arg0 : NULL); |
246 | maybe_add_expr (t: arg1, label: show_types ? &m_label_for_arg1 : NULL); |
247 | } |
248 | } |
249 | |
250 | /* Determine if binary_op_rich_location's ctor should attempt to make |
251 | a 3-location rich_location (the location of the operator and of |
252 | the 2 arguments), or fall back to a 1-location rich_location showing |
253 | just the combined location of the operation as a whole. */ |
254 | |
255 | bool |
256 | binary_op_rich_location::use_operator_loc_p (const op_location_t &loc, |
257 | tree arg0, tree arg1) |
258 | { |
259 | /* If we don't have a combined location, then use the operator location, |
260 | and try to add ranges for the operators. */ |
261 | if (loc.m_combined_loc == UNKNOWN_LOCATION) |
262 | return true; |
263 | |
264 | /* If we don't have the operator location, then use the |
265 | combined location. */ |
266 | if (loc.m_operator_loc == UNKNOWN_LOCATION) |
267 | return false; |
268 | |
269 | /* We have both operator location and combined location: only use the |
270 | operator location if we have locations for both arguments. */ |
271 | return (EXPR_HAS_LOCATION (arg0) |
272 | && EXPR_HAS_LOCATION (arg1)); |
273 | } |
274 | |