Skip to content

macros: Add env! macro #1113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions gcc/rust/expand/rust-macro-builtins.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions gcc/rust/util/rust-hir-map.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 ());
Expand Down
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
}