#include "rust-ast-full.h"
#include "rust-ast-visitor.h"
#include "rust-diagnostics.h"
+#include "rust-macro.h"
#include "rust-parse.h"
#include "rust-cfg-strip.h"
#include "rust-early-name-resolver.h"
for (auto &ent : matched_fragments)
matched_fragments_ptr.emplace (ent.first, ent.second.get ());
- return transcribe_rule (*matched_rule, invoc_token_tree,
+ return transcribe_rule (rules_def, *matched_rule, invoc_token_tree,
matched_fragments_ptr, semicolon, peek_context ());
}
AST::Fragment
MacroExpander::transcribe_rule (
- AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
+ AST::MacroRulesDefinition &definition, AST::MacroRule &match_rule,
+ AST::DelimTokenTree &invoc_token_tree,
std::map<std::string, MatchedFragmentContainer *> &matched_fragments,
AST::InvocKind invoc_kind, ContextType ctx)
{
auto invoc_stream = invoc_token_tree.to_token_stream ();
auto macro_rule_tokens = transcribe_tree.to_token_stream ();
- auto substitute_context
- = SubstituteCtx (invoc_stream, macro_rule_tokens, matched_fragments);
+ auto substitute_context = SubstituteCtx (invoc_stream, macro_rule_tokens,
+ matched_fragments, definition);
std::vector<std::unique_ptr<AST::Token>> substituted_tokens
= substitute_context.substitute_tokens ();
AST::DelimTokenTree &invoc_token_tree);
AST::Fragment transcribe_rule (
- AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
+ AST::MacroRulesDefinition &definition, AST::MacroRule &match_rule,
+ AST::DelimTokenTree &invoc_token_tree,
std::map<std::string, MatchedFragmentContainer *> &matched_fragments,
AST::InvocKind invoc_kind, ContextType ctx);
// <http://www.gnu.org/licenses/>.
#include "rust-macro-substitute-ctx.h"
+#include "input.h"
+#include "rust-hir-map.h"
+#include "rust-token.h"
namespace Rust {
+bool
+SubstituteCtx::substitute_dollar_crate (
+ std::vector<std::unique_ptr<AST::Token>> &expanded)
+{
+ auto &mappings = Analysis::Mappings::get ();
+
+ auto def_crate = mappings.lookup_macro_def_crate (definition.get_node_id ());
+ auto current_crate = mappings.get_current_crate ();
+
+ rust_assert (def_crate);
+
+ // If we're expanding a macro defined in the current crate which uses $crate,
+ // we can just replace the metavar with the `crate` path segment. Otherwise,
+ // use the fully qualified extern-crate lookup path `::<crate_name>`
+ if (*def_crate == current_crate)
+ {
+ expanded.push_back (std::make_unique<AST::Token> (
+ Rust::Token::make_identifier (UNKNOWN_LOCATION, "crate")));
+ }
+ else
+ {
+ auto name = mappings.get_crate_name (*def_crate);
+
+ rust_assert (name);
+
+ expanded.push_back (std::make_unique<AST::Token> (
+ Rust::Token::make (SCOPE_RESOLUTION, UNKNOWN_LOCATION)));
+ expanded.push_back (std::make_unique<AST::Token> (
+ Rust::Token::make_identifier (UNKNOWN_LOCATION, std::string (*name))));
+ }
+
+ return true;
+}
+
bool
SubstituteCtx::substitute_metavar (
std::unique_ptr<AST::Token> &metavar,
auto it = fragments.find (metavar_name);
if (it == fragments.end ())
{
- // fail to substitute
+ // fail to substitute, unless we are dealing with a special-case metavar
+ // like $crate
- // HACK: substitute ($ crate) => (crate)
- if (metavar->get_id () != CRATE)
- return false;
+ if (metavar->get_id () == CRATE)
+ return substitute_dollar_crate (expanded);
expanded.push_back (metavar->clone_token ());
- return true;
+
+ return false;
}
else
{
kv_match.second->get_fragments ().at (i).get ());
}
- auto substitute_context = SubstituteCtx (input, new_macro, sub_map);
+ auto substitute_context
+ = SubstituteCtx (input, new_macro, sub_map, definition);
auto new_tokens = substitute_context.substitute_tokens ();
// Skip the first repetition, but add the separator to the expanded
#include "rust-ast.h"
#include "rust-macro-expand.h"
+#include "rust-macro.h"
namespace Rust {
class SubstituteCtx
std::vector<std::unique_ptr<AST::Token>> &input;
std::vector<std::unique_ptr<AST::Token>> ¯o;
std::map<std::string, MatchedFragmentContainer *> &fragments;
+ AST::MacroRulesDefinition &definition;
/**
* Find the repetition amount to use when expanding a repetition, and
public:
SubstituteCtx (std::vector<std::unique_ptr<AST::Token>> &input,
std::vector<std::unique_ptr<AST::Token>> ¯o,
- std::map<std::string, MatchedFragmentContainer *> &fragments)
- : input (input), macro (macro), fragments (fragments)
+ std::map<std::string, MatchedFragmentContainer *> &fragments,
+ AST::MacroRulesDefinition &definition)
+ : input (input), macro (macro), fragments (fragments),
+ definition (definition)
{}
+ /**
+ * Special-case the $crate metavar to expand to the name of the crate in which
+ * the macro was defined.
+ *
+ * https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.hygiene.crate
+ *
+ *
+ * @param expanded Reference to a vector upon which expanded tokens will be
+ * pushed
+ *
+ * @return True if the substitution succeeded
+ */
+ bool
+ substitute_dollar_crate (std::vector<std::unique_ptr<AST::Token>> &expanded);
+
/**
* Substitute a metavariable by its given fragment in a transcribing context,
* i.e. replacing $var with the associated fragment.
* @param expanded Reference to a vector upon which expanded tokens will be
* pushed
*
- * @return True iff the substitution succeeded
+ * @return True if the substitution succeeded
*/
bool substitute_metavar (std::unique_ptr<AST::Token> &metavar,
std::vector<std::unique_ptr<AST::Token>> &expanded);
auto it = macroMappings.find (macro->get_node_id ());
rust_assert (it == macroMappings.end ());
- macroMappings[macro->get_node_id ()] = macro;
+ macroMappings[macro->get_node_id ()] = {macro, currentCrateNum};
}
tl::optional<AST::MacroRulesDefinition *>
if (it == macroMappings.end ())
return tl::nullopt;
- return it->second;
+ return it->second.first;
+}
+
+tl::optional<CrateNum>
+Mappings::lookup_macro_def_crate (NodeId id)
+{
+ auto it = macroMappings.find (id);
+ if (it == macroMappings.end ())
+ return tl::nullopt;
+
+ return it->second.second;
}
void
void insert_macro_def (AST::MacroRulesDefinition *macro);
tl::optional<AST::MacroRulesDefinition *> lookup_macro_def (NodeId id);
+ tl::optional<CrateNum> lookup_macro_def_crate (NodeId id);
void insert_macro_invocation (AST::MacroInvocation &invoc,
AST::MacroRulesDefinition *def);
std::map<CrateNum, std::set<HirId>> hirNodesWithinCrate;
// MBE macros
- std::map<NodeId, AST::MacroRulesDefinition *> macroMappings;
+ std::map<NodeId, std::pair<AST::MacroRulesDefinition *, CrateNum>>
+ macroMappings;
std::map<NodeId, AST::MacroRulesDefinition *> macroInvocations;
std::vector<NodeId> exportedMacros;
--- /dev/null
+macro_rules! foo {
+ () => {
+ $crate::inner::bar()
+ }
+}
+
+pub mod inner {
+ pub fn bar() { }
+}
+
+fn main() {
+ foo!();
+ crate::inner::bar();
+}
--- /dev/null
+macro_rules! foo {
+ () => {
+ $crate::bar()
+ }
+}
+
+pub fn bar() -> i32 { 1 }
+
+fn main() -> i32 {
+ foo!() - crate::bar()
+}