// Copyright (C) 2020-2024 Free Software Foundation, Inc.
// This file is part of GCC.
// GCC is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 3, or (at your option) any later
// version.
// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
// You should have received a copy of the GNU General Public License
// along with GCC; see the file COPYING3. If not see
// .
#include "rust-expand-visitor.h"
#include "rust-proc-macro.h"
#include "rust-attributes.h"
#include "rust-ast.h"
#include "rust-type.h"
#include "rust-derive.h"
namespace Rust {
bool
is_builtin (AST::Attribute &attr)
{
auto &segments = attr.get_path ().get_segments ();
return !segments.empty ()
&& !Analysis::BuiltinAttributeMappings::get ()
->lookup_builtin (segments[0].get_segment_name ())
.is_error ();
}
/* Expand all of the macro invocations currently contained in a crate */
void
ExpandVisitor::go (AST::Crate &crate)
{
visit (crate);
}
static std::unique_ptr
builtin_derive_item (AST::Item &item, const AST::Attribute &derive,
BuiltinMacro to_derive)
{
return AST::DeriveVisitor::derive (item, derive, to_derive);
}
static std::vector>
derive_item (AST::Item &item, AST::SimplePath &to_derive,
MacroExpander &expander)
{
std::vector> result;
auto frag = expander.expand_derive_proc_macro (item, to_derive);
if (!frag.is_error ())
{
for (auto &node : frag.get_nodes ())
{
switch (node.get_kind ())
{
case AST::SingleASTNode::ITEM:
result.push_back (node.take_item ());
break;
default:
rust_unreachable ();
}
}
}
return result;
}
static std::vector>
expand_item_attribute (AST::Item &item, AST::SimplePath &name,
MacroExpander &expander)
{
std::vector> result;
auto frag = expander.expand_attribute_proc_macro (item, name);
if (!frag.is_error ())
{
for (auto &node : frag.get_nodes ())
{
switch (node.get_kind ())
{
case AST::SingleASTNode::ITEM:
result.push_back (node.take_item ());
break;
default:
rust_unreachable ();
}
}
}
return result;
}
/* Helper function to expand a given attribute on a statement and collect back
* statements.
* T should be anything that can be used as a statement accepting outer
* attributes.
*/
template
static std::vector>
expand_stmt_attribute (T &statement, AST::SimplePath &attribute,
MacroExpander &expander)
{
std::vector> result;
auto frag = expander.expand_attribute_proc_macro (statement, attribute);
if (!frag.is_error ())
{
for (auto &node : frag.get_nodes ())
{
switch (node.get_kind ())
{
case AST::SingleASTNode::STMT:
result.push_back (node.take_stmt ());
break;
default:
rust_unreachable ();
}
}
}
return result;
}
void
expand_tail_expr (AST::BlockExpr &block_expr, MacroExpander &expander)
{
if (block_expr.has_tail_expr ())
{
auto tail = block_expr.take_tail_expr ();
auto attrs = tail->get_outer_attrs ();
bool changed = false;
for (auto it = attrs.begin (); it != attrs.end ();)
{
auto current = *it;
if (is_builtin (current))
{
it++;
}
else
{
it = attrs.erase (it);
changed = true;
auto new_stmts
= expand_stmt_attribute (block_expr, current.get_path (),
expander);
auto &stmts = block_expr.get_statements ();
std::move (new_stmts.begin (), new_stmts.end (),
std::inserter (stmts, stmts.end ()));
}
}
if (changed)
block_expr.normalize_tail_expr ();
else
block_expr.set_tail_expr (std::move (tail));
}
}
void
ExpandVisitor::expand_inner_items (
std::vector> &items)
{
expander.push_context (MacroExpander::ContextType::ITEM);
for (auto it = items.begin (); it != items.end (); it++)
{
auto &item = *it;
if (item->has_outer_attrs ())
{
auto &attrs = item->get_outer_attrs ();
for (auto attr_it = attrs.begin (); attr_it != attrs.end ();
/* erase => No increment*/)
{
auto current = *attr_it;
if (current.is_derive ())
{
current.parse_attr_to_meta_item ();
attr_it = attrs.erase (attr_it);
// Get traits to derive in the current attribute
auto traits_to_derive = current.get_traits_to_derive ();
for (auto &to_derive : traits_to_derive)
{
auto maybe_builtin = MacroBuiltin::builtins.lookup (
to_derive.get ().as_string ());
if (MacroBuiltin::builtins.is_iter_ok (maybe_builtin))
{
auto new_item
= builtin_derive_item (*item, current,
maybe_builtin->second);
// this inserts the derive *before* the item - is it a
// problem?
it = items.insert (it, std::move (new_item));
}
else
{
auto new_items
= derive_item (*item, to_derive, expander);
std::move (new_items.begin (), new_items.end (),
std::inserter (items, it));
}
}
}
else /* Attribute */
{
if (is_builtin (current))
{
attr_it++;
}
else
{
attr_it = attrs.erase (attr_it);
auto new_items
= expand_item_attribute (*item, current.get_path (),
expander);
it = items.erase (it);
std::move (new_items.begin (), new_items.end (),
std::inserter (items, it));
// TODO: Improve this ?
// item is invalid since it refers to now deleted,
// cancel the loop increment and break.
it--;
break;
}
}
}
}
}
std::function (AST::SingleASTNode)> extractor
= [] (AST::SingleASTNode node) { return node.take_item (); };
expand_macro_children (items, extractor);
expander.pop_context ();
}
void
ExpandVisitor::expand_inner_stmts (AST::BlockExpr &expr)
{
auto &stmts = expr.get_statements ();
expander.push_context (MacroExpander::ContextType::STMT);
for (auto it = stmts.begin (); it != stmts.end (); it++)
{
auto &stmt = *it;
// skip all non-item statements
if (stmt->get_stmt_kind () != AST::Stmt::Kind::Item)
continue;
auto &item = static_cast (*stmt.get ());
if (item.has_outer_attrs ())
{
auto &attrs = item.get_outer_attrs ();
for (auto attr_it = attrs.begin (); attr_it != attrs.end ();
/* erase => No increment*/)
{
auto current = *attr_it;
if (current.is_derive ())
{
attr_it = attrs.erase (attr_it);
// Get traits to derive in the current attribute
auto traits_to_derive = current.get_traits_to_derive ();
for (auto &to_derive : traits_to_derive)
{
auto maybe_builtin = MacroBuiltin::builtins.lookup (
to_derive.get ().as_string ());
if (MacroBuiltin::builtins.is_iter_ok (maybe_builtin))
{
auto new_item
= builtin_derive_item (item, current,
maybe_builtin->second);
// this inserts the derive *before* the item - is it a
// problem?
it = stmts.insert (it, std::move (new_item));
}
else
{
auto new_items
= derive_item (item, to_derive, expander);
std::move (new_items.begin (), new_items.end (),
std::inserter (stmts, it));
}
}
}
else /* Attribute */
{
if (is_builtin (current))
{
attr_it++;
}
else
{
attr_it = attrs.erase (attr_it);
auto new_items
= expand_stmt_attribute (item, current.get_path (),
expander);
it = stmts.erase (it);
std::move (new_items.begin (), new_items.end (),
std::inserter (stmts, it));
// TODO: Improve this ?
// item is invalid since it refers to now deleted,
// cancel the loop increment and break.
it--;
break;
}
}
}
}
}
if (!expr.has_tail_expr ())
expr.normalize_tail_expr ();
std::function (AST::SingleASTNode)> extractor
= [] (AST::SingleASTNode node) { return node.take_stmt (); };
expand_macro_children (stmts, extractor);
expander.pop_context ();
}
void
ExpandVisitor::maybe_expand_expr (std::unique_ptr &expr)
{
expander.push_context (MacroExpander::ContextType::EXPR);
expr->accept_vis (*this);
expander.pop_context ();
auto final_fragment = expander.take_expanded_fragment ();
if (final_fragment.should_expand ()
&& final_fragment.is_expression_fragment ())
expr = final_fragment.take_expression_fragment ();
}
void
ExpandVisitor::maybe_expand_type (std::unique_ptr &type)
{
expander.push_context (MacroExpander::ContextType::TYPE);
type->accept_vis (*this);
auto final_fragment = expander.take_expanded_fragment ();
if (final_fragment.should_expand () && final_fragment.is_type_fragment ())
type = final_fragment.take_type_fragment ();
expander.pop_context ();
}
// FIXME: Can this be refactored into a `scoped` method? Which takes a
// ContextType as parameter and a lambda? And maybe just an std::vector&?
void
ExpandVisitor::expand_struct_fields (std::vector &fields)
{
for (auto &field : fields)
{
maybe_expand_type (field.get_field_type ());
}
}
void
ExpandVisitor::expand_tuple_fields (std::vector &fields)
{
for (auto &field : fields)
maybe_expand_type (field.get_field_type ());
}
// FIXME: This can definitely be refactored with the method above
void
ExpandVisitor::expand_function_params (
std::vector> ¶ms)
{
for (auto &p : params)
visit (p);
}
void
ExpandVisitor::expand_generic_args (AST::GenericArgs &args)
{
for (auto &arg : args.get_generic_args ())
{
switch (arg.get_kind ())
{
case AST::GenericArg::Kind::Type:
maybe_expand_type (arg.get_type ());
break;
case AST::GenericArg::Kind::Const:
maybe_expand_expr (arg.get_expression ());
break;
default:
break;
// FIXME: Figure out what to do here if there is ambiguity. Since the
// resolver comes after the expansion, we need to figure out a way to
// strip ambiguous values here
// TODO: ARTHUR: Probably add a `mark_as_strip` method to `GenericArg`
// or something. This would clean up this whole thing
}
}
// FIXME: Can we have macro invocations in generic type bindings?
// expand binding args - strip sub-types only
// FIXME: ARTHUR: This needs a test! Foo-
for (auto &binding : args.get_binding_args ())
maybe_expand_type (binding.get_type ());
}
void
ExpandVisitor::expand_qualified_path_type (AST::QualifiedPathType &path_type)
{
maybe_expand_type (path_type.get_type ());
// FIXME: ARTHUR: Can we do macro expansion in there? Needs a test!
if (path_type.has_as_clause ())
path_type.get_as_type_path ().accept_vis (*this);
}
void
ExpandVisitor::expand_closure_params (std::vector ¶ms)
{
for (auto ¶m : params)
{
if (param.has_type_given ())
maybe_expand_type (param.get_type ());
}
}
void
ExpandVisitor::expand_where_clause (AST::WhereClause &where_clause)
{
for (auto &item : where_clause.get_items ())
visit (item);
}
void
ExpandVisitor::visit (AST::Crate &crate)
{
expand_inner_items (crate.items);
}
void
ExpandVisitor::visit (AST::DelimTokenTree &)
{}
void
ExpandVisitor::visit (AST::AttrInputMetaItemContainer &)
{}
void
ExpandVisitor::visit (AST::IdentifierExpr &ident_expr)
{}
void
ExpandVisitor::visit (AST::LifetimeParam &)
{}
void
ExpandVisitor::visit (AST::ConstGenericParam &)
{}
void
ExpandVisitor::visit (AST::MacroInvocation ¯o_invoc)
{
// TODO: Can we do the AST fragment replacing here? Probably not, right?
expander.expand_invoc (macro_invoc, macro_invoc.has_semicolon ());
}
void
ExpandVisitor::visit (AST::PathInExpression &path)
{
for (auto &segment : path.get_segments ())
if (segment.has_generic_args ())
expand_generic_args (segment.get_generic_args ());
}
void
ExpandVisitor::visit (AST::TypePathSegmentGeneric &segment)
{}
void
ExpandVisitor::visit (AST::TypePathSegmentFunction &segment)
{
auto &type_path_function = segment.get_type_path_function ();
for (auto &type : type_path_function.get_params ())
visit (type);
if (type_path_function.has_return_type ())
maybe_expand_type (type_path_function.get_return_type ());
}
void
ExpandVisitor::visit (AST::QualifiedPathInExpression &path)
{
expand_qualified_path_type (path.get_qualified_path_type ());
for (auto &segment : path.get_segments ())
if (segment.has_generic_args ())
expand_generic_args (segment.get_generic_args ());
}
void
ExpandVisitor::visit (AST::QualifiedPathInType &path)
{
expand_qualified_path_type (path.get_qualified_path_type ());
// this shouldn't strip any segments, but can strip inside them
for (auto &segment : path.get_segments ())
visit (segment);
}
void
ExpandVisitor::visit (AST::LiteralExpr &expr)
{}
void
ExpandVisitor::visit (AST::AttrInputLiteral &)
{}
void
ExpandVisitor::visit (AST::AttrInputMacro ¯o)
{
rust_sorry_at (UNDEF_LOCATION, "macros in attributes not supported");
}
void
ExpandVisitor::visit (AST::MetaItemLitExpr &)
{}
void
ExpandVisitor::visit (AST::MetaItemPathLit &)
{}
void
ExpandVisitor::visit (AST::ErrorPropagationExpr &expr)
{
visit (expr.get_propagating_expr ());
}
void
ExpandVisitor::visit (AST::ArithmeticOrLogicalExpr &expr)
{
maybe_expand_expr (expr.get_left_expr ());
maybe_expand_expr (expr.get_right_expr ());
}
void
ExpandVisitor::visit (AST::ComparisonExpr &expr)
{
maybe_expand_expr (expr.get_left_expr ());
maybe_expand_expr (expr.get_right_expr ());
}
void
ExpandVisitor::visit (AST::LazyBooleanExpr &expr)
{
maybe_expand_expr (expr.get_left_expr ());
maybe_expand_expr (expr.get_right_expr ());
}
void
ExpandVisitor::visit (AST::AssignmentExpr &expr)
{
maybe_expand_expr (expr.get_left_expr ());
maybe_expand_expr (expr.get_right_expr ());
}
void
ExpandVisitor::visit (AST::CompoundAssignmentExpr &expr)
{
maybe_expand_expr (expr.get_left_expr ());
maybe_expand_expr (expr.get_right_expr ());
}
void
ExpandVisitor::visit (AST::GroupedExpr &expr)
{
maybe_expand_expr (expr.get_expr_in_parens ());
}
void
ExpandVisitor::visit (AST::StructExprStruct &expr)
{}
void
ExpandVisitor::visit (AST::CallExpr &expr)
{
visit (expr.get_function_expr ());
for (auto ¶m : expr.get_params ())
maybe_expand_expr (param);
}
void
ExpandVisitor::visit (AST::MethodCallExpr &expr)
{
visit (expr.get_receiver_expr ());
for (auto ¶m : expr.get_params ())
maybe_expand_expr (param);
}
void
ExpandVisitor::visit (AST::ClosureExprInner &expr)
{
expand_closure_params (expr.get_params ());
visit (expr.get_definition_expr ());
}
void
ExpandVisitor::visit (AST::BlockExpr &expr)
{
expand_inner_stmts (expr);
expand_tail_expr (expr, expander);
if (expr.has_tail_expr ())
maybe_expand_expr (expr.get_tail_expr ());
}
void
ExpandVisitor::visit (AST::ClosureExprInnerTyped &expr)
{
expand_closure_params (expr.get_params ());
maybe_expand_type (expr.get_return_type ());
visit (expr.get_definition_block ());
}
void
ExpandVisitor::visit (AST::ContinueExpr &expr)
{}
void
ExpandVisitor::visit (AST::IfExpr &expr)
{
maybe_expand_expr (expr.get_condition_expr ());
visit (expr.get_if_block ());
}
void
ExpandVisitor::visit (AST::IfExprConseqElse &expr)
{
maybe_expand_expr (expr.get_condition_expr ());
visit (expr.get_if_block ());
visit (expr.get_else_block ());
}
void
ExpandVisitor::visit (AST::IfLetExpr &expr)
{
maybe_expand_expr (expr.get_value_expr ());
visit (expr.get_if_block ());
}
void
ExpandVisitor::visit (AST::IfLetExprConseqElse &expr)
{
maybe_expand_expr (expr.get_value_expr ());
visit (expr.get_if_block ());
visit (expr.get_else_block ());
}
void
ExpandVisitor::visit (AST::MatchExpr &expr)
{
visit (expr.get_scrutinee_expr ());
for (auto &match_case : expr.get_match_cases ())
{
auto &arm = match_case.get_arm ();
for (auto &pattern : arm.get_patterns ())
visit (pattern);
if (arm.has_match_arm_guard ())
maybe_expand_expr (arm.get_guard_expr ());
maybe_expand_expr (match_case.get_expr ());
}
}
void
ExpandVisitor::visit (AST::TypeParam ¶m)
{
for (auto &bound : param.get_type_param_bounds ())
visit (bound);
if (param.has_type ())
maybe_expand_type (param.get_type ());
}
void
ExpandVisitor::visit (AST::LifetimeWhereClauseItem &)
{}
void
ExpandVisitor::visit (AST::TypeBoundWhereClauseItem &item)
{
maybe_expand_type (item.get_type ());
for (auto &bound : item.get_type_param_bounds ())
visit (bound);
}
void
ExpandVisitor::visit (AST::ExternCrate &crate)
{}
void
ExpandVisitor::visit (AST::UseTreeGlob &)
{}
void
ExpandVisitor::visit (AST::UseTreeList &)
{}
void
ExpandVisitor::visit (AST::UseTreeRebind &)
{}
void
ExpandVisitor::visit (AST::UseDeclaration &use_decl)
{}
void
ExpandVisitor::visit (AST::Function &function)
{
if (function.has_body ())
visit_inner_using_attrs (
function, function.get_definition ().value ()->get_inner_attrs ());
for (auto ¶m : function.get_generic_params ())
visit (param);
expand_function_params (function.get_function_params ());
if (function.has_return_type ())
maybe_expand_type (function.get_return_type ());
if (function.has_where_clause ())
expand_where_clause (function.get_where_clause ());
if (function.has_body ())
visit (*function.get_definition ());
}
void
ExpandVisitor::visit (AST::StructStruct &struct_item)
{
for (auto &generic : struct_item.get_generic_params ())
visit (generic);
if (struct_item.has_where_clause ())
expand_where_clause (struct_item.get_where_clause ());
expand_struct_fields (struct_item.get_fields ());
}
void
ExpandVisitor::visit (AST::TupleStruct &tuple_struct)
{
for (auto &generic : tuple_struct.get_generic_params ())
visit (generic);
if (tuple_struct.has_where_clause ())
expand_where_clause (tuple_struct.get_where_clause ());
expand_tuple_fields (tuple_struct.get_fields ());
}
void
ExpandVisitor::visit (AST::EnumItem &item)
{}
void
ExpandVisitor::visit (AST::EnumItemTuple &item)
{
expand_tuple_fields (item.get_tuple_fields ());
}
void
ExpandVisitor::visit (AST::EnumItemStruct &item)
{
expand_struct_fields (item.get_struct_fields ());
}
void
ExpandVisitor::visit (AST::EnumItemDiscriminant &item)
{
maybe_expand_expr (item.get_expr ());
}
void
ExpandVisitor::visit (AST::Union &union_item)
{
for (auto &generic : union_item.get_generic_params ())
visit (generic);
expand_struct_fields (union_item.get_variants ());
}
void
ExpandVisitor::visit (AST::ConstantItem &const_item)
{
maybe_expand_type (const_item.get_type ());
if (const_item.has_expr ())
maybe_expand_expr (const_item.get_expr ());
}
void
ExpandVisitor::visit (AST::StaticItem &static_item)
{
maybe_expand_type (static_item.get_type ());
maybe_expand_expr (static_item.get_expr ());
}
void
ExpandVisitor::visit (AST::TraitItemConst &const_item)
{
maybe_expand_type (const_item.get_type ());
if (const_item.has_expr ())
maybe_expand_expr (const_item.get_expr ());
}
void
ExpandVisitor::visit (AST::Trait &trait)
{
for (auto &generic : trait.get_generic_params ())
visit (generic);
for (auto &bound : trait.get_type_param_bounds ())
visit (bound);
if (trait.has_where_clause ())
expand_where_clause (trait.get_where_clause ());
expander.push_context (MacroExpander::ContextType::TRAIT);
std::function (AST::SingleASTNode)>
extractor
= [] (AST::SingleASTNode node) { return node.take_trait_item (); };
expand_macro_children (MacroExpander::ContextType::TRAIT,
trait.get_trait_items (), extractor);
expander.pop_context ();
}
void
ExpandVisitor::visit (AST::InherentImpl &impl)
{
visit_inner_attrs (impl);
// just expand sub-stuff - can't actually strip generic params themselves
for (auto &generic : impl.get_generic_params ())
visit (generic);
// FIXME: Is that correct? How do we test that?
expander.push_context (MacroExpander::ContextType::ITEM);
maybe_expand_type (impl.get_type ());
expander.pop_context ();
if (impl.has_where_clause ())
expand_where_clause (impl.get_where_clause ());
std::function (AST::SingleASTNode)>
extractor = [] (AST::SingleASTNode node) { return node.take_impl_item (); };
expand_macro_children (MacroExpander::ContextType::IMPL,
impl.get_impl_items (), extractor);
}
void
ExpandVisitor::visit (AST::TraitImpl &impl)
{
visit_inner_attrs (impl);
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : impl.get_generic_params ())
visit (param);
// FIXME: Is that correct? How do we test that?
expander.push_context (MacroExpander::ContextType::ITEM);
maybe_expand_type (impl.get_type ());
expander.pop_context ();
visit (impl.get_trait_path ());
if (impl.has_where_clause ())
expand_where_clause (impl.get_where_clause ());
std::function (AST::SingleASTNode)>
extractor
= [] (AST::SingleASTNode node) { return node.take_trait_impl_item (); };
expand_macro_children (MacroExpander::ContextType::TRAIT_IMPL,
impl.get_impl_items (), extractor);
}
void
ExpandVisitor::visit (AST::ExternalTypeItem &item)
{}
void
ExpandVisitor::visit (AST::ExternalStaticItem &static_item)
{
maybe_expand_type (static_item.get_type ());
}
void
ExpandVisitor::visit (AST::ExternalFunctionItem &item)
{
for (auto ¶m : item.get_generic_params ())
visit (param);
for (auto ¶m : item.get_function_params ())
if (!param.is_variadic ())
maybe_expand_type (param.get_type ());
if (item.has_return_type ())
maybe_expand_type (item.get_return_type ());
if (item.has_where_clause ())
expand_where_clause (item.get_where_clause ());
}
void
ExpandVisitor::visit (AST::ExternBlock &block)
{
visit_inner_attrs (block);
std::function (AST::SingleASTNode)>
extractor
= [] (AST::SingleASTNode node) { return node.take_external_item (); };
expand_macro_children (MacroExpander::ContextType::EXTERN,
block.get_extern_items (), extractor);
}
void
ExpandVisitor::visit (AST::MacroMatchRepetition &)
{}
void
ExpandVisitor::visit (AST::MacroMatcher &)
{}
void
ExpandVisitor::visit (AST::MacroRulesDefinition &rules_def)
{}
void
ExpandVisitor::visit (AST::MetaItemPath &)
{}
void
ExpandVisitor::visit (AST::MetaItemSeq &)
{}
void
ExpandVisitor::visit (AST::MetaListPaths &)
{}
void
ExpandVisitor::visit (AST::MetaListNameValueStr &)
{}
void
ExpandVisitor::visit (AST::StructPatternFieldIdent &field)
{}
void
ExpandVisitor::visit (AST::GroupedPattern &pattern)
{
visit (pattern.get_pattern_in_parens ());
}
void
ExpandVisitor::visit (AST::LetStmt &stmt)
{
visit (stmt.get_pattern ());
if (stmt.has_type ())
maybe_expand_type (stmt.get_type ());
if (stmt.has_init_expr ())
maybe_expand_expr (stmt.get_init_expr ());
}
void
ExpandVisitor::visit (AST::ExprStmt &stmt)
{
maybe_expand_expr (stmt.get_expr ());
}
void
ExpandVisitor::visit (AST::BareFunctionType &type)
{
for (auto ¶m : type.get_function_params ())
{
maybe_expand_type (param.get_type ());
}
if (type.has_return_type ())
visit (type.get_return_type ());
}
void
ExpandVisitor::visit (AST::FunctionParam ¶m)
{
maybe_expand_type (param.get_type ());
}
void
ExpandVisitor::visit (AST::SelfParam ¶m)
{
/* TODO: maybe check for invariants being violated - e.g. both type and
* lifetime? */
if (param.has_type ())
maybe_expand_type (param.get_type ());
}
template
void
ExpandVisitor::expand_inner_attribute (T &item, AST::SimplePath &path)
{
// FIXME: Retrieve path from segments + local use statements instead of string
expander.expand_attribute_proc_macro (item, path);
}
template
void
ExpandVisitor::visit_inner_using_attrs (T &item,
std::vector &attrs)
{
for (auto it = attrs.begin (); it != attrs.end (); /* erase => No increment*/)
{
auto current = *it;
if (!is_builtin (current) && !current.is_derive ())
{
it = attrs.erase (it);
expand_inner_attribute (item, current.get_path ());
}
else
{
it++;
}
}
}
template
void
ExpandVisitor::visit_inner_attrs (T &item)
{
visit_inner_using_attrs (item, item.get_inner_attrs ());
}
} // namespace Rust