go: update builtin function attributes
[official-gcc.git] / gcc / rust / expand / rust-macro-builtins-helpers.cc
blobd44efccd195931af8094df87eca5891e86ed1eec
1 // Copyright (C) 2020-2025 Free Software Foundation, Inc.
3 // This file is part of GCC.
5 // GCC is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU General Public License as published by the Free
7 // Software Foundation; either version 3, or (at your option) any later
8 // version.
10 // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 // for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with GCC; see the file COPYING3. If not see
17 // <http://www.gnu.org/licenses/>.
19 #include "rust-macro-builtins-helpers.h"
21 namespace Rust {
23 std::string
24 make_macro_path_str (BuiltinMacro kind)
26 auto str = MacroBuiltin::builtins.lookup (kind);
27 rust_assert (str.has_value ());
29 return str.value ();
32 std::vector<std::unique_ptr<AST::MacroInvocation>>
33 check_for_eager_invocations (
34 std::vector<std::unique_ptr<AST::Expr>> &expressions)
36 std::vector<std::unique_ptr<AST::MacroInvocation>> pending;
38 for (auto &expr : expressions)
39 if (expr->get_ast_kind () == AST::Kind::MACRO_INVOCATION)
40 pending.emplace_back (std::unique_ptr<AST::MacroInvocation> (
41 static_cast<AST::MacroInvocation *> (expr->clone_expr ().release ())));
43 return pending;
47 // Shorthand function for creating unique_ptr tokens
49 std::unique_ptr<AST::Token>
50 make_token (const TokenPtr tok)
52 return std::unique_ptr<AST::Token> (new AST::Token (tok));
55 std::unique_ptr<AST::Expr>
56 make_string (location_t locus, std::string value)
58 return std::unique_ptr<AST::Expr> (
59 new AST::LiteralExpr (value, AST::Literal::STRING,
60 PrimitiveCoreType::CORETYPE_STR, {}, locus));
63 // TODO: Is this correct?
64 AST::Fragment
65 make_eager_builtin_invocation (
66 BuiltinMacro kind, location_t locus, AST::DelimTokenTree arguments,
67 std::vector<std::unique_ptr<AST::MacroInvocation>> &&pending_invocations)
69 auto path_str = make_macro_path_str (kind);
71 std::unique_ptr<AST::Expr> node = AST::MacroInvocation::Builtin (
72 kind,
73 AST::MacroInvocData (AST::SimplePath (
74 {AST::SimplePathSegment (path_str, locus)}),
75 std::move (arguments)),
76 {}, locus, std::move (pending_invocations));
78 return AST::Fragment ({AST::SingleASTNode (std::move (node))},
79 arguments.to_token_stream ());
82 /* Match the end token of a macro given the start delimiter of the macro */
83 TokenId
84 macro_end_token (AST::DelimTokenTree &invoc_token_tree,
85 Parser<MacroInvocLexer> &parser)
87 auto last_token_id = TokenId::RIGHT_CURLY;
88 switch (invoc_token_tree.get_delim_type ())
90 case AST::DelimType::PARENS:
91 last_token_id = TokenId::RIGHT_PAREN;
92 rust_assert (parser.skip_token (LEFT_PAREN));
93 break;
95 case AST::DelimType::CURLY:
96 rust_assert (parser.skip_token (LEFT_CURLY));
97 break;
99 case AST::DelimType::SQUARE:
100 last_token_id = TokenId::RIGHT_SQUARE;
101 rust_assert (parser.skip_token (LEFT_SQUARE));
102 break;
105 return last_token_id;
108 // Expand and then extract a string literal from the macro
109 std::unique_ptr<AST::LiteralExpr>
110 try_extract_string_literal_from_fragment (const location_t &parent_locus,
111 std::unique_ptr<AST::Expr> &node)
113 auto maybe_lit = static_cast<AST::LiteralExpr *> (node.get ());
114 if (!node || !node->is_literal ()
115 || maybe_lit->get_lit_type () != AST::Literal::STRING)
117 rust_error_at (parent_locus, "argument must be a string literal");
118 if (node)
119 rust_inform (node->get_locus (), "expanded from here");
120 return nullptr;
122 return std::unique_ptr<AST::LiteralExpr> (
123 static_cast<AST::LiteralExpr *> (node->clone_expr ().release ()));
126 std::vector<std::unique_ptr<AST::Expr>>
127 try_expand_many_expr (Parser<MacroInvocLexer> &parser,
128 const TokenId last_token_id, MacroExpander *expander,
129 bool &has_error)
131 auto restrictions = Rust::ParseRestrictions ();
132 // stop parsing when encountered a braces/brackets
133 restrictions.expr_can_be_null = true;
134 // we can't use std::optional, so...
135 auto result = std::vector<std::unique_ptr<AST::Expr>> ();
136 auto empty_expr = std::vector<std::unique_ptr<AST::Expr>> ();
138 auto first_token = parser.peek_current_token ()->get_id ();
139 if (first_token == COMMA)
141 rust_error_at (parser.peek_current_token ()->get_locus (),
142 "expected expression, found %<,%>");
143 has_error = true;
144 return empty_expr;
147 while (parser.peek_current_token ()->get_id () != last_token_id
148 && parser.peek_current_token ()->get_id () != END_OF_FILE)
150 auto expr = parser.parse_expr (AST::AttrVec (), restrictions);
151 // something must be so wrong that the expression could not be parsed
152 rust_assert (expr);
153 result.push_back (std::move (expr));
155 auto next_token = parser.peek_current_token ();
156 if (!parser.skip_token (COMMA) && next_token->get_id () != last_token_id)
158 rust_error_at (next_token->get_locus (), "expected token: %<,%>");
159 // TODO: is this recoverable? to avoid crashing the parser in the next
160 // fragment we have to exit early here
161 has_error = true;
162 return empty_expr;
166 return result;
169 // Parse a single string literal from the given delimited token tree,
170 // and return the LiteralExpr for it. Allow for an optional trailing comma,
171 // but otherwise enforce that these are the only tokens.
172 // FIXME(Arthur): This function needs a rework - it should not emit errors, it
173 // should probably be smaller
174 std::unique_ptr<AST::Expr>
175 parse_single_string_literal (BuiltinMacro kind,
176 AST::DelimTokenTree &invoc_token_tree,
177 location_t invoc_locus, MacroExpander *expander)
179 MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
180 Parser<MacroInvocLexer> parser (lex);
182 auto last_token_id = macro_end_token (invoc_token_tree, parser);
184 std::unique_ptr<AST::LiteralExpr> lit_expr = nullptr;
185 std::unique_ptr<AST::MacroInvocation> macro_invoc = nullptr;
187 if (parser.peek_current_token ()->get_id () == STRING_LITERAL)
189 lit_expr = parser.parse_literal_expr ();
190 parser.maybe_skip_token (COMMA);
191 if (parser.peek_current_token ()->get_id () != last_token_id)
193 lit_expr = nullptr;
194 rust_error_at (invoc_locus, "macro takes 1 argument");
197 else if (parser.peek_current_token ()->get_id () == last_token_id)
198 rust_error_at (invoc_locus, "macro takes 1 argument");
199 else
201 macro_invoc = parser.parse_macro_invocation (AST::AttrVec ());
203 parser.maybe_skip_token (COMMA);
204 if (parser.peek_current_token ()->get_id () != last_token_id)
206 lit_expr = nullptr;
207 rust_error_at (invoc_locus, "macro takes 1 argument");
210 if (macro_invoc != nullptr)
212 auto path_str = make_macro_path_str (kind);
214 auto pending_invocations
215 = std::vector<std::unique_ptr<AST::MacroInvocation>> ();
217 pending_invocations.push_back (std::move (macro_invoc));
219 return AST::MacroInvocation::Builtin (
220 kind,
221 AST::MacroInvocData (AST::SimplePath ({AST::SimplePathSegment (
222 path_str, invoc_locus)}),
223 std::move (invoc_token_tree)),
224 {}, invoc_locus, std::move (pending_invocations));
226 else
228 rust_error_at (invoc_locus, "argument must be a string literal or a "
229 "macro which expands to a string");
233 parser.skip_token (last_token_id);
235 return std::unique_ptr<AST::Expr> (std::move (lit_expr));
238 /* Treat PATH as a path relative to the source file currently being
239 compiled, and return the absolute path for it. */
240 std::string
241 source_relative_path (std::string path, location_t locus)
243 std::string compile_fname = LOCATION_FILE (locus);
245 auto dir_separator_pos = compile_fname.rfind (file_separator);
247 /* If there is no file_separator in the path, use current dir ('.'). */
248 std::string dirname;
249 if (dir_separator_pos == std::string::npos)
250 dirname = std::string (".") + file_separator;
251 else
252 dirname = compile_fname.substr (0, dir_separator_pos) + file_separator;
254 return dirname + path;
257 /* Read the full contents of the file FILENAME and return them in a vector.
258 FIXME: platform specific. */
259 tl::optional<std::vector<uint8_t>>
260 load_file_bytes (location_t invoc_locus, const char *filename)
262 RAIIFile file_wrap (filename);
263 if (file_wrap.get_raw () == nullptr)
265 rust_error_at (invoc_locus, "cannot open filename %s: %m", filename);
266 return tl::nullopt;
269 FILE *f = file_wrap.get_raw ();
270 fseek (f, 0L, SEEK_END);
271 long fsize = ftell (f);
272 fseek (f, 0L, SEEK_SET);
274 std::vector<uint8_t> buf (fsize);
276 if (fsize > 0 && fread (&buf[0], fsize, 1, f) != 1)
278 rust_error_at (invoc_locus, "error reading file %s: %m", filename);
279 return std::vector<uint8_t> ();
282 return buf;
284 } // namespace Rust