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