Skip to content

Commit 73e017f

Browse files
bors[bot]omachota
andauthored
Merge #1113
1113: macros: Add env! macro r=CohenArthur a=omachota Added the `env!()` macro and relevant test cases Fixes: #977 Signed-off-by: Ondřej Machota <[email protected]> Co-authored-by: Ondřej Machota <[email protected]>
2 parents 6032412 + 1e6e427 commit 73e017f

File tree

5 files changed

+119
-0
lines changed

5 files changed

+119
-0
lines changed

gcc/rust/expand/rust-macro-builtins.cc

+70
Original file line numberDiff line numberDiff line change
@@ -306,4 +306,74 @@ MacroBuiltin::concat (Location invoc_locus, AST::MacroInvocData &invoc)
306306
return AST::ASTFragment ({node});
307307
}
308308

309+
/* Expand builtin macro env!(), which inspects an environment variable at
310+
compile time. */
311+
312+
AST::ASTFragment
313+
MacroBuiltin::env (Location invoc_locus, AST::MacroInvocData &invoc)
314+
{
315+
auto invoc_token_tree = invoc.get_delim_tok_tree ();
316+
MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
317+
Parser<MacroInvocLexer> parser (std::move (lex));
318+
319+
auto last_token_id = macro_end_token (invoc_token_tree, parser);
320+
321+
if (parser.peek_current_token ()->get_id () != STRING_LITERAL)
322+
{
323+
if (parser.peek_current_token ()->get_id () == last_token_id)
324+
rust_error_at (invoc_locus, "env! takes 1 or 2 arguments");
325+
else
326+
rust_error_at (parser.peek_current_token ()->get_locus (),
327+
"argument must be a string literal");
328+
return AST::ASTFragment::create_error ();
329+
}
330+
331+
auto lit_expr = parser.parse_literal_expr ();
332+
auto comma_skipped = parser.maybe_skip_token (COMMA);
333+
334+
std::unique_ptr<AST::LiteralExpr> error_expr = nullptr;
335+
336+
if (parser.peek_current_token ()->get_id () != last_token_id)
337+
{
338+
if (!comma_skipped)
339+
{
340+
rust_error_at (parser.peek_current_token ()->get_locus (),
341+
"expected token: %<,%>");
342+
return AST::ASTFragment::create_error ();
343+
}
344+
if (parser.peek_current_token ()->get_id () != STRING_LITERAL)
345+
{
346+
rust_error_at (parser.peek_current_token ()->get_locus (),
347+
"argument must be a string literal");
348+
return AST::ASTFragment::create_error ();
349+
}
350+
351+
error_expr = parser.parse_literal_expr ();
352+
parser.maybe_skip_token (COMMA);
353+
}
354+
355+
if (parser.peek_current_token ()->get_id () != last_token_id)
356+
{
357+
rust_error_at (invoc_locus, "env! takes 1 or 2 arguments");
358+
return AST::ASTFragment::create_error ();
359+
}
360+
361+
parser.skip_token (last_token_id);
362+
363+
auto env_value = getenv (lit_expr->as_string ().c_str ());
364+
365+
if (env_value == nullptr)
366+
{
367+
if (error_expr == nullptr)
368+
rust_error_at (invoc_locus, "environment variable %qs not defined",
369+
lit_expr->as_string ().c_str ());
370+
else
371+
rust_error_at (invoc_locus, "%s", error_expr->as_string ().c_str ());
372+
return AST::ASTFragment::create_error ();
373+
}
374+
375+
auto node = AST::SingleASTNode (make_string (invoc_locus, env_value));
376+
return AST::ASTFragment ({node});
377+
}
378+
309379
} // namespace Rust

gcc/rust/expand/rust-macro-builtins.h

+3
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ class MacroBuiltin
8989

9090
static AST::ASTFragment concat (Location invoc_locus,
9191
AST::MacroInvocData &invoc);
92+
93+
static AST::ASTFragment env (Location invoc_locus,
94+
AST::MacroInvocData &invoc);
9295
};
9396
} // namespace Rust
9497

gcc/rust/util/rust-hir-map.cc

+1
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,7 @@ Mappings::insert_macro_def (AST::MacroRulesDefinition *macro)
755755
{"include_str", MacroBuiltin::include_str},
756756
{"compile_error", MacroBuiltin::compile_error},
757757
{"concat", MacroBuiltin::concat},
758+
{"env", MacroBuiltin::env},
758759
};
759760

760761
auto builtin = builtin_macros.find (macro->get_rule_name ());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
macro_rules! env {
2+
() => {{}};
3+
}
4+
5+
fn main () {
6+
let message = "error message";
7+
env! (message); // { dg-error "argument must be a string literal" "" }
8+
env! (); // { dg-error "env! takes 1 or 2 arguments" "" }
9+
env! (,); // { dg-error "argument must be a string literal" "" }
10+
env! (1); // { dg-error "argument must be a string literal" "" }
11+
env! ("NOT_DEFINED"); // { dg-error "environment variable 'NOT_DEFINED' not defined" "" }
12+
env! ("NOT_DEFINED",); // { dg-error "environment variable 'NOT_DEFINED' not defined" "" }
13+
env! ("NOT_DEFINED", 1); // { dg-error "argument must be a string literal" "" }
14+
env! ("NOT_DEFINED", "two", "three"); // { dg-error "env! takes 1 or 2 arguments" "" }
15+
env! ("NOT_DEFINED" "expected error message"); // { dg-error "expected token: ','" "" }
16+
env! ("NOT_DEFINED", "expected error message"); // { dg-error "expected error message" "" }
17+
env! ("NOT_DEFINED", "expected error message",); // { dg-error "expected error message" "" }
18+
env! (1, "two"); // { dg-error "argument must be a string literal" "" }
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// { dg-output "VALUE\nVALUE\n" }
2+
// { dg-set-compiler-env-var ENV_MACRO_TEST "VALUE" }
3+
4+
macro_rules! env {
5+
() => {{}};
6+
}
7+
8+
extern "C" {
9+
fn printf(fmt: *const i8, ...);
10+
}
11+
12+
fn print(s: &str) {
13+
printf("%s\n" as *const str as *const i8, s as *const str as *const i8);
14+
}
15+
16+
fn main() -> i32 {
17+
let val0 = env!("ENV_MACRO_TEST");
18+
19+
print(val0);
20+
21+
let val1 = env!("ENV_MACRO_TEST",);
22+
23+
print(val1);
24+
25+
0
26+
}

0 commit comments

Comments
 (0)