Skip to content

Commit

Permalink
macros: Add env! macro
Browse files Browse the repository at this point in the history
Signed-off-by: Ondřej Machota <ondrejmachota@gmail.com>
  • Loading branch information
omachota committed Apr 13, 2022
1 parent e9ab95c commit 1e6e427
Showing 5 changed files with 119 additions and 0 deletions.
70 changes: 70 additions & 0 deletions gcc/rust/expand/rust-macro-builtins.cc
Original file line number Diff line number Diff line change
@@ -306,4 +306,74 @@ MacroBuiltin::concat (Location invoc_locus, AST::MacroInvocData &invoc)
return AST::ASTFragment ({node});
}

/* Expand builtin macro env!(), which inspects an environment variable at
compile time. */

AST::ASTFragment
MacroBuiltin::env (Location invoc_locus, AST::MacroInvocData &invoc)
{
auto invoc_token_tree = invoc.get_delim_tok_tree ();
MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
Parser<MacroInvocLexer> parser (std::move (lex));

auto last_token_id = macro_end_token (invoc_token_tree, parser);

if (parser.peek_current_token ()->get_id () != STRING_LITERAL)
{
if (parser.peek_current_token ()->get_id () == last_token_id)
rust_error_at (invoc_locus, "env! takes 1 or 2 arguments");
else
rust_error_at (parser.peek_current_token ()->get_locus (),
"argument must be a string literal");
return AST::ASTFragment::create_error ();
}

auto lit_expr = parser.parse_literal_expr ();
auto comma_skipped = parser.maybe_skip_token (COMMA);

std::unique_ptr<AST::LiteralExpr> error_expr = nullptr;

if (parser.peek_current_token ()->get_id () != last_token_id)
{
if (!comma_skipped)
{
rust_error_at (parser.peek_current_token ()->get_locus (),
"expected token: %<,%>");
return AST::ASTFragment::create_error ();
}
if (parser.peek_current_token ()->get_id () != STRING_LITERAL)
{
rust_error_at (parser.peek_current_token ()->get_locus (),
"argument must be a string literal");
return AST::ASTFragment::create_error ();
}

error_expr = parser.parse_literal_expr ();
parser.maybe_skip_token (COMMA);
}

if (parser.peek_current_token ()->get_id () != last_token_id)
{
rust_error_at (invoc_locus, "env! takes 1 or 2 arguments");
return AST::ASTFragment::create_error ();
}

parser.skip_token (last_token_id);

auto env_value = getenv (lit_expr->as_string ().c_str ());

if (env_value == nullptr)
{
if (error_expr == nullptr)
rust_error_at (invoc_locus, "environment variable %qs not defined",
lit_expr->as_string ().c_str ());
else
rust_error_at (invoc_locus, "%s", error_expr->as_string ().c_str ());
return AST::ASTFragment::create_error ();
}

auto node = AST::SingleASTNode (make_string (invoc_locus, env_value));
return AST::ASTFragment ({node});
}

} // namespace Rust
3 changes: 3 additions & 0 deletions gcc/rust/expand/rust-macro-builtins.h
Original file line number Diff line number Diff line change
@@ -89,6 +89,9 @@ class MacroBuiltin

static AST::ASTFragment concat (Location invoc_locus,
AST::MacroInvocData &invoc);

static AST::ASTFragment env (Location invoc_locus,
AST::MacroInvocData &invoc);
};
} // namespace Rust

1 change: 1 addition & 0 deletions gcc/rust/util/rust-hir-map.cc
Original file line number Diff line number Diff line change
@@ -755,6 +755,7 @@ Mappings::insert_macro_def (AST::MacroRulesDefinition *macro)
{"include_str", MacroBuiltin::include_str},
{"compile_error", MacroBuiltin::compile_error},
{"concat", MacroBuiltin::concat},
{"env", MacroBuiltin::env},
};

auto builtin = builtin_macros.find (macro->get_rule_name ());
19 changes: 19 additions & 0 deletions gcc/testsuite/rust/compile/builtin_macro_env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
macro_rules! env {
() => {{}};
}

fn main () {
let message = "error message";
env! (message); // { dg-error "argument must be a string literal" "" }
env! (); // { dg-error "env! takes 1 or 2 arguments" "" }
env! (,); // { dg-error "argument must be a string literal" "" }
env! (1); // { dg-error "argument must be a string literal" "" }
env! ("NOT_DEFINED"); // { dg-error "environment variable 'NOT_DEFINED' not defined" "" }
env! ("NOT_DEFINED",); // { dg-error "environment variable 'NOT_DEFINED' not defined" "" }
env! ("NOT_DEFINED", 1); // { dg-error "argument must be a string literal" "" }
env! ("NOT_DEFINED", "two", "three"); // { dg-error "env! takes 1 or 2 arguments" "" }
env! ("NOT_DEFINED" "expected error message"); // { dg-error "expected token: ','" "" }
env! ("NOT_DEFINED", "expected error message"); // { dg-error "expected error message" "" }
env! ("NOT_DEFINED", "expected error message",); // { dg-error "expected error message" "" }
env! (1, "two"); // { dg-error "argument must be a string literal" "" }
}
26 changes: 26 additions & 0 deletions gcc/testsuite/rust/execute/torture/builtin_macro_env.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// { dg-output "VALUE\nVALUE\n" }
// { dg-set-compiler-env-var ENV_MACRO_TEST "VALUE" }

macro_rules! env {
() => {{}};
}

extern "C" {
fn printf(fmt: *const i8, ...);
}

fn print(s: &str) {
printf("%s\n" as *const str as *const i8, s as *const str as *const i8);
}

fn main() -> i32 {
let val0 = env!("ENV_MACRO_TEST");

print(val0);

let val1 = env!("ENV_MACRO_TEST",);

print(val1);

0
}

0 comments on commit 1e6e427

Please sign in to comment.