Branch data Line data Source code
1 : : /* This file is part of GCC.
2 : :
3 : : GCC is free software; you can redistribute it and/or modify
4 : : it under the terms of the GNU General Public License as published by
5 : : the Free Software Foundation; either version 3, or (at your option)
6 : : any later version.
7 : :
8 : : GCC is distributed in the hope that it will be useful,
9 : : but WITHOUT ANY WARRANTY; without even the implied warranty of
10 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 : : GNU General Public License for more details.
12 : :
13 : : You should have received a copy of the GNU General Public License
14 : : along with GCC; see the file COPYING3. If not see
15 : : <http://www.gnu.org/licenses/>. */
16 : :
17 : : #include "rust-parse.h"
18 : : #include "rust-linemap.h"
19 : : #include "rust-diagnostics.h"
20 : : #include "rust-token.h"
21 : : #include "rust-attribute-values.h"
22 : :
23 : : namespace Rust {
24 : :
25 : : std::string
26 : 616 : extract_module_path (const AST::AttrVec &inner_attrs,
27 : : const AST::AttrVec &outer_attrs, const std::string &name)
28 : : {
29 : 616 : AST::Attribute path_attr = AST::Attribute::create_empty ();
30 : 715 : for (const auto &attr : inner_attrs)
31 : : {
32 : 101 : if (attr.get_path ().as_string () == Values::Attributes::PATH)
33 : : {
34 : 2 : path_attr = attr;
35 : 2 : break;
36 : : }
37 : : }
38 : :
39 : : // Here, we found a path attribute, but it has no associated string. This is
40 : : // invalid
41 : 616 : if (!path_attr.is_empty () && !path_attr.has_attr_input ())
42 : : {
43 : 0 : rust_error_at (
44 : : path_attr.get_locus (),
45 : : // Split the format string so that -Wformat-diag does not complain...
46 : : "path attributes must contain a filename: '%s'", "#![path = \"file\"]");
47 : 0 : return name;
48 : : }
49 : :
50 : 720 : for (const auto &attr : outer_attrs)
51 : : {
52 : 152 : if (attr.get_path ().as_string () == Values::Attributes::PATH)
53 : : {
54 : 48 : path_attr = attr;
55 : 48 : break;
56 : : }
57 : : }
58 : :
59 : : // We didn't find a path attribute. This is not an error, there simply isn't
60 : : // one present
61 : 616 : if (path_attr.is_empty ())
62 : 567 : return name;
63 : :
64 : : // Here, we found a path attribute, but it has no associated string. This is
65 : : // invalid
66 : 49 : if (!path_attr.has_attr_input ())
67 : : {
68 : 7 : rust_error_at (
69 : : path_attr.get_locus (),
70 : : // Split the format string so that -Wformat-diag does not complain...
71 : : "path attributes must contain a filename: '%s'", "#[path = \"file\"]");
72 : 7 : return name;
73 : : }
74 : :
75 : 42 : auto path_value = path_attr.get_attr_input ().as_string ();
76 : :
77 : : // At this point, the 'path' is of the following format: '= "<file.rs>"'
78 : : // We need to remove the equal sign and only keep the actual filename.
79 : : // In order to do this, we can simply go through the string until we find
80 : : // a character that is not an equal sign or whitespace
81 : 42 : auto filename_begin = path_value.find_first_not_of ("=\t ");
82 : :
83 : 42 : auto path = path_value.substr (filename_begin);
84 : :
85 : : // On windows, the path might mix '/' and '\' separators. Replace the
86 : : // UNIX-like separators by MSDOS separators to make sure the path will resolve
87 : : // properly.
88 : : //
89 : : // Source: rustc compiler
90 : : // (https://github.com/rust-lang/rust/blob/9863bf51a52b8e61bcad312f81b5193d53099f9f/compiler/rustc_expand/src/module.rs#L174)
91 : : #if defined(HAVE_DOS_BASED_FILE_SYSTEM)
92 : : path.replace ('/', '\\');
93 : : #endif /* HAVE_DOS_BASED_FILE_SYSTEM */
94 : :
95 : 42 : return path;
96 : 42 : }
97 : :
98 : : template <typename T>
99 : : static bool
100 : 120 : contains (std::vector<T> &vec, T elm)
101 : : {
102 : 120 : return std::find (vec.begin (), vec.end (), elm) != vec.end ();
103 : : }
104 : :
105 : : /**
106 : : * Avoid UB by calling .front() and .back() on empty containers...
107 : : */
108 : :
109 : : template <typename T>
110 : : static const T *
111 : 22 : get_back_ptr (const std::vector<std::unique_ptr<T>> &values)
112 : : {
113 : 22 : if (values.empty ())
114 : : return nullptr;
115 : :
116 : 22 : return values.back ().get ();
117 : : }
118 : :
119 : : template <typename T>
120 : : static const T *
121 : 0 : get_front_ptr (const std::vector<std::unique_ptr<T>> &values)
122 : : {
123 : 15 : if (values.empty ())
124 : : return nullptr;
125 : :
126 : 15 : return values.front ().get ();
127 : : }
128 : :
129 : : static bool
130 : 4 : peculiar_fragment_match_compatible_fragment (
131 : : const AST::MacroFragSpec &last_spec, const AST::MacroFragSpec &spec,
132 : : location_t match_locus)
133 : : {
134 : 4 : static std::unordered_map<AST::MacroFragSpec::Kind,
135 : : std::vector<AST::MacroFragSpec::Kind>>
136 : : fragment_follow_set
137 : : = {{AST::MacroFragSpec::PATH, {AST::MacroFragSpec::BLOCK}},
138 : 4 : {AST::MacroFragSpec::TY, {AST::MacroFragSpec::BLOCK}},
139 : 4 : {AST::MacroFragSpec::VIS,
140 : : {AST::MacroFragSpec::IDENT, AST::MacroFragSpec::TY,
141 : 20 : AST::MacroFragSpec::PATH}}};
142 : :
143 : 4 : auto is_valid
144 : 4 : = contains (fragment_follow_set[last_spec.get_kind ()], spec.get_kind ());
145 : :
146 : 4 : if (!is_valid)
147 : 1 : rust_error_at (
148 : : match_locus,
149 : : "fragment specifier %qs is not allowed after %qs fragments",
150 : 2 : spec.as_string ().c_str (), last_spec.as_string ().c_str ());
151 : :
152 : 4 : return is_valid;
153 : : }
154 : :
155 : : static bool
156 : 137 : peculiar_fragment_match_compatible (const AST::MacroMatchFragment &last_match,
157 : : const AST::MacroMatch &match)
158 : : {
159 : 137 : static std::unordered_map<AST::MacroFragSpec::Kind, std::vector<TokenId>>
160 : : follow_set
161 : : = {{AST::MacroFragSpec::EXPR, {MATCH_ARROW, COMMA, SEMICOLON}},
162 : 106 : {AST::MacroFragSpec::STMT, {MATCH_ARROW, COMMA, SEMICOLON}},
163 : 106 : {AST::MacroFragSpec::PAT, {MATCH_ARROW, COMMA, EQUAL, PIPE, IF, IN}},
164 : 106 : {AST::MacroFragSpec::PATH,
165 : : {MATCH_ARROW, COMMA, EQUAL, PIPE, SEMICOLON, COLON, RIGHT_ANGLE,
166 : : RIGHT_SHIFT, LEFT_SQUARE, LEFT_CURLY, AS, WHERE}},
167 : 106 : {AST::MacroFragSpec::TY,
168 : : {MATCH_ARROW, COMMA, EQUAL, PIPE, SEMICOLON, COLON, RIGHT_ANGLE,
169 : : RIGHT_SHIFT, LEFT_SQUARE, LEFT_CURLY, AS, WHERE}},
170 : 106 : {AST::MacroFragSpec::VIS,
171 : : {COMMA,
172 : : IDENTIFIER,
173 : : LEFT_PAREN,
174 : : LEFT_SQUARE,
175 : : EXCLAM,
176 : : ASTERISK,
177 : : AMP,
178 : : LOGICAL_AND,
179 : : QUESTION_MARK,
180 : : LIFETIME,
181 : : LEFT_ANGLE,
182 : : LEFT_SHIFT,
183 : : UNDERSCORE,
184 : : ABSTRACT,
185 : : AS,
186 : : ASYNC,
187 : : AUTO,
188 : : BECOME,
189 : : BOX,
190 : : BREAK,
191 : : CONST,
192 : : CONTINUE,
193 : : CRATE,
194 : : DO,
195 : : DYN,
196 : : ELSE,
197 : : ENUM_KW,
198 : : EXTERN_KW,
199 : : FALSE_LITERAL,
200 : : FINAL_KW,
201 : : FN_KW,
202 : : FOR,
203 : : IF,
204 : : IMPL,
205 : : IN,
206 : : LET,
207 : : LOOP,
208 : : MACRO,
209 : : MATCH_KW,
210 : : MOD,
211 : : MOVE,
212 : : MUT,
213 : : OVERRIDE_KW,
214 : : PUB,
215 : : REF,
216 : : RETURN_KW,
217 : : SELF_ALIAS,
218 : : SELF,
219 : : STATIC_KW,
220 : : STRUCT_KW,
221 : : SUPER,
222 : : TRAIT,
223 : : TRUE_LITERAL,
224 : : TRY,
225 : : TYPE,
226 : : TYPEOF,
227 : : UNSAFE,
228 : : UNSIZED,
229 : : USE,
230 : : VIRTUAL,
231 : : WHERE,
232 : : WHILE,
233 : 879 : YIELD}}};
234 : :
235 : 137 : location_t error_locus = match.get_match_locus ();
236 : 137 : std::string kind_str = "fragment";
237 : 137 : auto &allowed_toks = follow_set[last_match.get_frag_spec ().get_kind ()];
238 : :
239 : : // There are two behaviors to handle here: If the follow-up match is a token,
240 : : // we want to check if it is allowed.
241 : : // If it is a fragment, repetition or matcher then we know that it will be
242 : : // an error.
243 : : // For repetitions and matchers we want to extract a proper location to report
244 : : // the error.
245 : 137 : switch (match.get_macro_match_type ())
246 : : {
247 : 115 : case AST::MacroMatch::Tok: {
248 : 115 : auto tok = static_cast<const AST::Token *> (&match);
249 : 115 : if (contains (allowed_toks, tok->get_id ()))
250 : : return true;
251 : 8 : kind_str = "token `"
252 : 16 : + std::string (get_token_description (tok->get_id ())) + "`";
253 : 8 : error_locus = tok->get_match_locus ();
254 : 8 : break;
255 : : }
256 : : break;
257 : 15 : case AST::MacroMatch::Repetition: {
258 : 15 : auto repetition
259 : : = static_cast<const AST::MacroMatchRepetition *> (&match);
260 : 15 : auto &matches = repetition->get_matches ();
261 : 15 : auto first_frag = get_front_ptr (matches);
262 : 15 : if (first_frag)
263 : 15 : return peculiar_fragment_match_compatible (last_match, *first_frag);
264 : : break;
265 : : }
266 : 1 : case AST::MacroMatch::Matcher: {
267 : 1 : auto matcher = static_cast<const AST::MacroMatcher *> (&match);
268 : 1 : auto first_token = matcher->get_delim_type ();
269 : 1 : TokenId delim_id;
270 : 1 : switch (first_token)
271 : : {
272 : : case AST::PARENS:
273 : : delim_id = LEFT_PAREN;
274 : : break;
275 : : case AST::SQUARE:
276 : : delim_id = LEFT_SQUARE;
277 : : break;
278 : : case AST::CURLY:
279 : : delim_id = LEFT_CURLY;
280 : : break;
281 : 0 : default:
282 : 0 : rust_unreachable ();
283 : 1 : break;
284 : : }
285 : 1 : if (contains (allowed_toks, delim_id))
286 : : return true;
287 : 2 : kind_str = "token `" + std::string (get_token_description (delim_id))
288 : 1 : + "` at start of matcher";
289 : 1 : error_locus = matcher->get_match_locus ();
290 : 1 : break;
291 : : }
292 : 6 : case AST::MacroMatch::Fragment: {
293 : 6 : auto last_spec = last_match.get_frag_spec ();
294 : 6 : auto fragment = static_cast<const AST::MacroMatchFragment *> (&match);
295 : 6 : if (last_spec.has_follow_set_fragment_restrictions ())
296 : 4 : return peculiar_fragment_match_compatible_fragment (
297 : 8 : last_spec, fragment->get_frag_spec (), match.get_match_locus ());
298 : : }
299 : 2 : break;
300 : : }
301 : :
302 : 11 : rust_error_at (error_locus, "%s is not allowed after %qs fragment",
303 : : kind_str.c_str (),
304 : 11 : last_match.get_frag_spec ().as_string ().c_str ());
305 : 11 : auto allowed_toks_str
306 : 22 : = "`" + std::string (get_token_description (allowed_toks[0])) + "`";
307 : 93 : for (size_t i = 1; i < allowed_toks.size (); i++)
308 : 82 : allowed_toks_str
309 : 164 : += ", `" + std::string (get_token_description (allowed_toks[i])) + "`";
310 : :
311 : 11 : rust_inform (error_locus, "allowed tokens are %s", allowed_toks_str.c_str ());
312 : :
313 : 11 : return false;
314 : 11 : }
315 : :
316 : : bool
317 : 439 : is_match_compatible (const AST::MacroMatch &last_match,
318 : : const AST::MacroMatch &match)
319 : : {
320 : 461 : const AST::MacroMatch *new_last = nullptr;
321 : :
322 : : // We want to "extract" the concerning matches. In cases such as matchers and
323 : : // repetitions, we actually store multiple matchers, but are only concerned
324 : : // about the follow-set ambiguities of certain elements.
325 : : // There are some cases where we can short-circuit the algorithm: There will
326 : : // never be restrictions on token literals, or on certain fragments which do
327 : : // not have a set of follow-restrictions.
328 : :
329 : 461 : switch (last_match.get_macro_match_type ())
330 : : {
331 : : // This is our main stop condition: When we are finally looking at the
332 : : // last match (or its actual last component), and it is a fragment, it
333 : : // may contain some follow up restrictions.
334 : 175 : case AST::MacroMatch::Fragment: {
335 : 175 : auto fragment
336 : : = static_cast<const AST::MacroMatchFragment *> (&last_match);
337 : 175 : if (fragment->get_frag_spec ().has_follow_set_restrictions ())
338 : 122 : return peculiar_fragment_match_compatible (*fragment, match);
339 : : else
340 : : return true;
341 : : }
342 : 22 : case AST::MacroMatch::Repetition: {
343 : : // A repetition on the left hand side means we want to make sure the
344 : : // last match of the repetition is compatible with the new match
345 : 22 : auto repetition
346 : : = static_cast<const AST::MacroMatchRepetition *> (&last_match);
347 : 22 : new_last = get_back_ptr (repetition->get_matches ());
348 : : // If there are no matches in the matcher, then it can be followed by
349 : : // anything
350 : 22 : if (!new_last)
351 : : return true;
352 : : break;
353 : : }
354 : : case AST::MacroMatch::Matcher:
355 : : case AST::MacroMatch::Tok:
356 : : return true;
357 : : }
358 : :
359 : 0 : rust_assert (new_last);
360 : :
361 : : // We check recursively until we find a terminating condition
362 : : // FIXME: Does expansion depth/limit matter here?
363 : : return is_match_compatible (*new_last, match);
364 : : }
365 : : } // namespace Rust
|