// Copyright (C) 2020-2022 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-macro-expand.h"
#include "rust-ast-full.h"
#include "rust-ast-visitor.h"
#include "rust-diagnostics.h"
#include "rust-parse.h"
namespace Rust {
// Visitor used to expand attributes.
class AttrVisitor : public AST::ASTVisitor
{
private:
MacroExpander &expander;
public:
AttrVisitor (MacroExpander &expander) : expander (expander) {}
void expand_struct_fields (std::vector &fields)
{
for (auto it = fields.begin (); it != fields.end ();)
{
auto &field = *it;
auto &field_attrs = field.get_outer_attrs ();
expander.expand_cfg_attrs (field_attrs);
if (expander.fails_cfg_with_expand (field_attrs))
{
it = fields.erase (it);
continue;
}
// expand sub-types of type, but can't strip type itself
auto &type = field.get_field_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (),
"cannot strip type in this position");
// if nothing else happens, increment
++it;
}
}
void expand_tuple_fields (std::vector &fields)
{
for (auto it = fields.begin (); it != fields.end ();)
{
auto &field = *it;
auto &field_attrs = field.get_outer_attrs ();
expander.expand_cfg_attrs (field_attrs);
if (expander.fails_cfg_with_expand (field_attrs))
{
it = fields.erase (it);
continue;
}
// expand sub-types of type, but can't strip type itself
auto &type = field.get_field_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (),
"cannot strip type in this position");
// if nothing else happens, increment
++it;
}
}
void expand_function_params (std::vector ¶ms)
{
for (auto it = params.begin (); it != params.end ();)
{
auto ¶m = *it;
auto ¶m_attrs = param.get_outer_attrs ();
expander.expand_cfg_attrs (param_attrs);
if (expander.fails_cfg_with_expand (param_attrs))
{
it = params.erase (it);
continue;
}
// TODO: should an unwanted strip lead to break out of loop?
auto &pattern = param.get_pattern ();
pattern->accept_vis (*this);
if (pattern->is_marked_for_strip ())
rust_error_at (pattern->get_locus (),
"cannot strip pattern in this position");
auto &type = param.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (),
"cannot strip type in this position");
// increment
++it;
}
}
void expand_generic_args (AST::GenericArgs &args)
{
// lifetime args can't be expanded
// expand type args - strip sub-types only
for (auto &type : args.get_type_args ())
{
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (),
"cannot strip type in this position");
}
// expand binding args - strip sub-types only
for (auto &binding : args.get_binding_args ())
{
auto &type = binding.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (),
"cannot strip type in this position");
}
}
void expand_qualified_path_type (AST::QualifiedPathType &path_type)
{
auto &type = path_type.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (), "cannot strip type in this position");
if (path_type.has_as_clause ())
{
auto &type_path = path_type.get_as_type_path ();
visit (type_path);
if (type_path.is_marked_for_strip ())
rust_error_at (type_path.get_locus (),
"cannot strip type path in this position");
}
}
void expand_closure_params (std::vector ¶ms)
{
for (auto it = params.begin (); it != params.end ();)
{
auto ¶m = *it;
auto ¶m_attrs = param.get_outer_attrs ();
expander.expand_cfg_attrs (param_attrs);
if (expander.fails_cfg_with_expand (param_attrs))
{
it = params.erase (it);
continue;
}
auto &pattern = param.get_pattern ();
pattern->accept_vis (*this);
if (pattern->is_marked_for_strip ())
rust_error_at (pattern->get_locus (),
"cannot strip pattern in this position");
if (param.has_type_given ())
{
auto &type = param.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (),
"cannot strip type in this position");
}
// increment if found nothing else so far
++it;
}
}
void expand_self_param (AST::SelfParam &self_param)
{
if (self_param.has_type ())
{
auto &type = self_param.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (),
"cannot strip type in this position");
}
/* TODO: maybe check for invariants being violated - e.g. both type and
* lifetime? */
}
void expand_where_clause (AST::WhereClause &where_clause)
{
// items cannot be stripped conceptually, so just accept visitor
for (auto &item : where_clause.get_items ())
item->accept_vis (*this);
}
void expand_trait_function_decl (AST::TraitFunctionDecl &decl)
{
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : decl.get_generic_params ())
param->accept_vis (*this);
/* strip function parameters if required - this is specifically
* allowed by spec */
expand_function_params (decl.get_function_params ());
if (decl.has_return_type ())
{
auto &return_type = decl.get_return_type ();
return_type->accept_vis (*this);
if (return_type->is_marked_for_strip ())
rust_error_at (return_type->get_locus (),
"cannot strip type in this position");
}
if (decl.has_where_clause ())
expand_where_clause (decl.get_where_clause ());
}
void expand_trait_method_decl (AST::TraitMethodDecl &decl)
{
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : decl.get_generic_params ())
param->accept_vis (*this);
/* assuming you can't strip self param - wouldn't be a method
* anymore. spec allows outer attrs on self param, but doesn't
* specify whether cfg is used. */
expand_self_param (decl.get_self_param ());
/* strip function parameters if required - this is specifically
* allowed by spec */
expand_function_params (decl.get_function_params ());
if (decl.has_return_type ())
{
auto &return_type = decl.get_return_type ();
return_type->accept_vis (*this);
if (return_type->is_marked_for_strip ())
rust_error_at (return_type->get_locus (),
"cannot strip type in this position");
}
if (decl.has_where_clause ())
expand_where_clause (decl.get_where_clause ());
}
template void expand_pointer_allow_strip (T &values)
{
for (auto it = values.begin (); it != values.end ();)
{
auto &value = *it;
// mark for stripping if required
value->accept_vis (*this);
if (value->is_marked_for_strip ())
it = values.erase (it);
else
++it;
}
}
void visit (AST::Token &) override
{
// shouldn't require?
}
void visit (AST::DelimTokenTree &) override
{
// shouldn't require?
}
void visit (AST::AttrInputMetaItemContainer &) override
{
// shouldn't require?
}
void visit (AST::IdentifierExpr &ident_expr) override
{
// strip test based on outer attrs
expander.expand_cfg_attrs (ident_expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (ident_expr.get_outer_attrs ()))
{
ident_expr.mark_for_strip ();
return;
}
}
void visit (AST::Lifetime &) override
{
// shouldn't require?
}
void visit (AST::LifetimeParam &) override
{
// supposedly does not require - cfg does nothing
}
void visit (AST::MacroInvocationSemi ¯o_invoc) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (macro_invoc.get_outer_attrs ());
if (expander.fails_cfg_with_expand (macro_invoc.get_outer_attrs ()))
{
macro_invoc.mark_for_strip ();
return;
}
// can't strip simple path
// I don't think any macro token trees can be stripped in any way
// TODO: maybe have cfg! macro stripping behaviour here?
expander.expand_invoc_semi (macro_invoc);
// we need to visit the expanded fragments since it may need cfg expansion
// and it may be recursive
for (auto &node : macro_invoc.get_fragment ().get_nodes ())
node.accept_vis (*this);
}
void visit (AST::PathInExpression &path) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (path.get_outer_attrs ());
if (expander.fails_cfg_with_expand (path.get_outer_attrs ()))
{
path.mark_for_strip ();
return;
}
for (auto &segment : path.get_segments ())
{
if (segment.has_generic_args ())
expand_generic_args (segment.get_generic_args ());
}
}
void visit (AST::TypePathSegment &) override
{
// shouldn't require
}
void visit (AST::TypePathSegmentGeneric &segment) override
{
// TODO: strip inside generic args
if (!segment.has_generic_args ())
return;
expand_generic_args (segment.get_generic_args ());
}
void visit (AST::TypePathSegmentFunction &segment) override
{
auto &type_path_function = segment.get_type_path_function ();
for (auto &type : type_path_function.get_params ())
{
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (),
"cannot strip type in this position");
}
if (type_path_function.has_return_type ())
{
auto &return_type = type_path_function.get_return_type ();
return_type->accept_vis (*this);
if (return_type->is_marked_for_strip ())
rust_error_at (return_type->get_locus (),
"cannot strip type in this position");
}
}
void visit (AST::TypePath &path) override
{
// this shouldn't strip any segments, but can strip inside them
for (auto &segment : path.get_segments ())
segment->accept_vis (*this);
}
void visit (AST::QualifiedPathInExpression &path) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (path.get_outer_attrs ());
if (expander.fails_cfg_with_expand (path.get_outer_attrs ()))
{
path.mark_for_strip ();
return;
}
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 visit (AST::QualifiedPathInType &path) override
{
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 ())
segment->accept_vis (*this);
}
void visit (AST::LiteralExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
}
void visit (AST::AttrInputLiteral &) override
{
// shouldn't require?
}
void visit (AST::MetaItemLitExpr &) override
{
// shouldn't require?
}
void visit (AST::MetaItemPathLit &) override
{
// shouldn't require?
}
void visit (AST::BorrowExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip any internal sub-expressions - expression itself isn't
* allowed to have external attributes in this position so can't be
* stripped. */
auto &borrowed_expr = expr.get_borrowed_expr ();
borrowed_expr->accept_vis (*this);
if (borrowed_expr->is_marked_for_strip ())
rust_error_at (borrowed_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::DereferenceExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip any internal sub-expressions - expression itself isn't
* allowed to have external attributes in this position so can't be
* stripped. */
auto &dereferenced_expr = expr.get_dereferenced_expr ();
dereferenced_expr->accept_vis (*this);
if (dereferenced_expr->is_marked_for_strip ())
rust_error_at (dereferenced_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::ErrorPropagationExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip any internal sub-expressions - expression itself isn't
* allowed to have external attributes in this position so can't be
* stripped. */
auto &propagating_expr = expr.get_propagating_expr ();
propagating_expr->accept_vis (*this);
if (propagating_expr->is_marked_for_strip ())
rust_error_at (propagating_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::NegationExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip any internal sub-expressions - expression itself isn't
* allowed to have external attributes in this position so can't be
* stripped. */
auto &negated_expr = expr.get_negated_expr ();
negated_expr->accept_vis (*this);
if (negated_expr->is_marked_for_strip ())
rust_error_at (negated_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::ArithmeticOrLogicalExpr &expr) override
{
/* outer attributes never allowed before these. while cannot strip
* two direct descendant expressions, can strip ones below that */
/* should have no possibility for outer attrs as would be parsed
* with outer expr */
expr.get_left_expr ()->accept_vis (*this);
/* should syntactically not have outer attributes, though this may
* not have worked in practice */
expr.get_right_expr ()->accept_vis (*this);
// ensure that they are not marked for strip
if (expr.get_left_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_left_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes are never allowed "
"before binary op exprs");
if (expr.get_right_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_right_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::ComparisonExpr &expr) override
{
/* outer attributes never allowed before these. while cannot strip
* two direct descendant expressions, can strip ones below that */
/* should have no possibility for outer attrs as would be parsed
* with outer expr */
expr.get_left_expr ()->accept_vis (*this);
/* should syntactically not have outer attributes, though this may
* not have worked in practice */
expr.get_right_expr ()->accept_vis (*this);
// ensure that they are not marked for strip
if (expr.get_left_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_left_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes are never allowed "
"before binary op exprs");
if (expr.get_right_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_right_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::LazyBooleanExpr &expr) override
{
/* outer attributes never allowed before these. while cannot strip
* two direct descendant expressions, can strip ones below that */
/* should have no possibility for outer attrs as would be parsed
* with outer expr */
expr.get_left_expr ()->accept_vis (*this);
/* should syntactically not have outer attributes, though this may
* not have worked in practice */
expr.get_right_expr ()->accept_vis (*this);
// ensure that they are not marked for strip
if (expr.get_left_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_left_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes are never allowed "
"before binary op exprs");
if (expr.get_right_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_right_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::TypeCastExpr &expr) override
{
/* outer attributes never allowed before these. while cannot strip
* direct descendant expression, can strip ones below that */
auto &casted_expr = expr.get_casted_expr ();
/* should have no possibility for outer attrs as would be parsed
* with outer expr */
casted_expr->accept_vis (*this);
// ensure that they are not marked for strip
if (casted_expr->is_marked_for_strip ())
rust_error_at (casted_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes are never allowed before cast exprs");
// TODO: strip sub-types of type
auto &type = expr.get_type_to_cast_to ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (), "cannot strip type in this position");
}
void visit (AST::AssignmentExpr &expr) override
{
/* outer attributes never allowed before these. while cannot strip
* two direct descendant expressions, can strip ones below that */
/* should have no possibility for outer attrs as would be parsed
* with outer expr */
expr.get_left_expr ()->accept_vis (*this);
/* should syntactically not have outer attributes, though this may
* not have worked in practice */
expr.get_right_expr ()->accept_vis (*this);
// ensure that they are not marked for strip
if (expr.get_left_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_left_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes are never allowed "
"before binary op exprs");
if (expr.get_right_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_right_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::CompoundAssignmentExpr &expr) override
{
/* outer attributes never allowed before these. while cannot strip
* two direct descendant expressions, can strip ones below that */
/* should have no possibility for outer attrs as would be parsed
* with outer expr */
expr.get_left_expr ()->accept_vis (*this);
/* should syntactically not have outer attributes, though this may
* not have worked in practice */
expr.get_right_expr ()->accept_vis (*this);
// ensure that they are not marked for strip
if (expr.get_left_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_left_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes are never allowed "
"before binary op exprs");
if (expr.get_right_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_right_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::GroupedExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip test based on inner attrs - spec says these are inner
* attributes, not outer attributes of inner expr */
expander.expand_cfg_attrs (expr.get_inner_attrs ());
if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip any internal sub-expressions - expression itself isn't
* allowed to have external attributes in this position so can't be
* stripped. */
auto &inner_expr = expr.get_expr_in_parens ();
inner_expr->accept_vis (*this);
if (inner_expr->is_marked_for_strip ())
rust_error_at (inner_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::ArrayElemsValues &elems) override
{
/* apparently outer attributes are allowed in "elements of array
* expressions" according to spec */
expand_pointer_allow_strip (elems.get_values ());
}
void visit (AST::ArrayElemsCopied &elems) override
{
/* apparently outer attributes are allowed in "elements of array
* expressions" according to spec. on the other hand, it would not
* make conceptual sense to be able to remove either expression. As
* such, not implementing. TODO clear up the ambiguity here */
// only intend stripping for internal sub-expressions
auto &copied_expr = elems.get_elem_to_copy ();
copied_expr->accept_vis (*this);
if (copied_expr->is_marked_for_strip ())
rust_error_at (copied_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
auto ©_count = elems.get_num_copies ();
copy_count->accept_vis (*this);
if (copy_count->is_marked_for_strip ())
rust_error_at (copy_count->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::ArrayExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip test based on inner attrs - spec says there are separate
* inner attributes, not just outer attributes of inner exprs */
expander.expand_cfg_attrs (expr.get_inner_attrs ());
if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* assuming you can't strip away the ArrayElems type, but can strip
* internal expressions and whatever */
expr.get_array_elems ()->accept_vis (*this);
}
void visit (AST::ArrayIndexExpr &expr) override
{
/* it is unclear whether outer attributes are supposed to be
* allowed, but conceptually it wouldn't make much sense, but
* having expansion code anyway. TODO */
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip any internal sub-expressions - expression itself isn't
* allowed to have external attributes in this position so can't be
* stripped. */
auto &array_expr = expr.get_array_expr ();
array_expr->accept_vis (*this);
if (array_expr->is_marked_for_strip ())
rust_error_at (array_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
auto &index_expr = expr.get_index_expr ();
index_expr->accept_vis (*this);
if (index_expr->is_marked_for_strip ())
rust_error_at (index_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::TupleExpr &expr) override
{
/* according to spec, outer attributes are allowed on "elements of
* tuple expressions" */
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip test based on inner attrs - spec says these are inner
* attributes, not outer attributes of inner expr */
expander.expand_cfg_attrs (expr.get_inner_attrs ());
if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* apparently outer attributes are allowed in "elements of tuple
* expressions" according to spec */
expand_pointer_allow_strip (expr.get_tuple_elems ());
}
void visit (AST::TupleIndexExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* wouldn't strip this directly (as outer attrs should be
* associated with this level), but any sub-expressions would be
* stripped. Thus, no need to erase when strip check called. */
auto &tuple_expr = expr.get_tuple_expr ();
tuple_expr->accept_vis (*this);
if (tuple_expr->is_marked_for_strip ())
rust_error_at (tuple_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::StructExprStruct &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip test based on inner attrs - spec says these are inner
* attributes, not outer attributes of inner expr */
expander.expand_cfg_attrs (expr.get_inner_attrs ());
if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
{
expr.mark_for_strip ();
return;
}
// strip sub-exprs of path
auto &struct_name = expr.get_struct_name ();
visit (struct_name);
if (struct_name.is_marked_for_strip ())
rust_error_at (struct_name.get_locus (),
"cannot strip path in this position");
}
void visit (AST::StructExprFieldIdentifier &) override
{
// as no attrs (at moment, at least), no stripping possible
}
void visit (AST::StructExprFieldIdentifierValue &field) override
{
/* as no attrs possible (at moment, at least), only sub-expression
* stripping is possible */
auto &value = field.get_value ();
value->accept_vis (*this);
if (value->is_marked_for_strip ())
rust_error_at (value->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::StructExprFieldIndexValue &field) override
{
/* as no attrs possible (at moment, at least), only sub-expression
* stripping is possible */
auto &value = field.get_value ();
value->accept_vis (*this);
if (value->is_marked_for_strip ())
rust_error_at (value->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::StructExprStructFields &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip test based on inner attrs - spec says these are inner
* attributes, not outer attributes of inner expr */
expander.expand_cfg_attrs (expr.get_inner_attrs ());
if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
{
expr.mark_for_strip ();
return;
}
// strip sub-exprs of path
auto &struct_name = expr.get_struct_name ();
visit (struct_name);
if (struct_name.is_marked_for_strip ())
rust_error_at (struct_name.get_locus (),
"cannot strip path in this position");
/* spec does not specify whether expressions are allowed to be
* stripped at top level of struct fields, but I wouldn't think
* that they would be, so operating under the assumption that only
* sub-expressions can be stripped. */
for (auto &field : expr.get_fields ())
{
field->accept_vis (*this);
// shouldn't strip in this
}
/* struct base presumably can't be stripped, as the '..' is before
* the expression. as such, can only strip sub-expressions. */
if (expr.has_struct_base ())
{
auto &base_struct_expr = expr.get_struct_base ().get_base_struct ();
base_struct_expr->accept_vis (*this);
if (base_struct_expr->is_marked_for_strip ())
rust_error_at (base_struct_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
}
void visit (AST::StructExprStructBase &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip test based on inner attrs - spec says these are inner
* attributes, not outer attributes of inner expr */
expander.expand_cfg_attrs (expr.get_inner_attrs ());
if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
{
expr.mark_for_strip ();
return;
}
// strip sub-exprs of path
auto &struct_name = expr.get_struct_name ();
visit (struct_name);
if (struct_name.is_marked_for_strip ())
rust_error_at (struct_name.get_locus (),
"cannot strip path in this position");
/* struct base presumably can't be stripped, as the '..' is before
* the expression. as such, can only strip sub-expressions. */
rust_assert (!expr.get_struct_base ().is_invalid ());
auto &base_struct_expr = expr.get_struct_base ().get_base_struct ();
base_struct_expr->accept_vis (*this);
if (base_struct_expr->is_marked_for_strip ())
rust_error_at (base_struct_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::CallExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* should not be outer attrs on "function" expression - outer attrs
* should be associated with call expr as a whole. only sub-expr
* expansion is possible. */
auto &function = expr.get_function_expr ();
function->accept_vis (*this);
if (function->is_marked_for_strip ())
rust_error_at (function->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
/* spec says outer attributes are specifically allowed for elements
* of call expressions, so full stripping possible */
expand_pointer_allow_strip (expr.get_params ());
}
void visit (AST::MethodCallExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* should not be outer attrs on "receiver" expression - outer attrs
* should be associated with call expr as a whole. only sub-expr
* expansion is possible. */
auto &receiver = expr.get_receiver_expr ();
receiver->accept_vis (*this);
if (receiver->is_marked_for_strip ())
rust_error_at (receiver->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
auto &method_name = expr.get_method_name ();
if (method_name.has_generic_args ())
expand_generic_args (method_name.get_generic_args ());
/* spec says outer attributes are specifically allowed for elements
* of method call expressions, so full stripping possible */
expand_pointer_allow_strip (expr.get_params ());
}
void visit (AST::FieldAccessExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* should not be outer attrs on "receiver" expression - outer attrs
* should be associated with field expr as a whole. only sub-expr
* expansion is possible. */
auto &receiver = expr.get_receiver_expr ();
receiver->accept_vis (*this);
if (receiver->is_marked_for_strip ())
rust_error_at (receiver->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::ClosureExprInner &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip closure parameters if required - this is specifically
* allowed by spec */
expand_closure_params (expr.get_params ());
// can't strip expression itself, but can strip sub-expressions
auto &definition_expr = expr.get_definition_expr ();
definition_expr->accept_vis (*this);
if (definition_expr->is_marked_for_strip ())
rust_error_at (definition_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::BlockExpr &expr) override
{
expander.push_context (MacroExpander::BLOCK);
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
expander.pop_context ();
return;
}
/* strip test based on inner attrs - spec says there are inner
* attributes, not just outer attributes of inner stmts */
expander.expand_cfg_attrs (expr.get_inner_attrs ());
if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
{
expr.mark_for_strip ();
expander.pop_context ();
return;
}
// strip all statements
expand_pointer_allow_strip (expr.get_statements ());
// strip tail expression if exists - can actually fully remove it
if (expr.has_tail_expr ())
{
auto &tail_expr = expr.get_tail_expr ();
tail_expr->accept_vis (*this);
if (tail_expr->is_marked_for_strip ())
expr.strip_tail_expr ();
}
expander.pop_context ();
}
void visit (AST::ClosureExprInnerTyped &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* strip closure parameters if required - this is specifically
* allowed by spec */
expand_closure_params (expr.get_params ());
// can't strip return type, but can strip sub-types
auto &type = expr.get_return_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (), "cannot strip type in this position");
// can't strip expression itself, but can strip sub-expressions
auto &definition_block = expr.get_definition_block ();
definition_block->accept_vis (*this);
if (definition_block->is_marked_for_strip ())
rust_error_at (definition_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
}
void visit (AST::ContinueExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
}
void visit (AST::BreakExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* spec does not say that you can have outer attributes on
* expression, so assuming you can't. stripping for sub-expressions
* is the only thing that can be done */
if (expr.has_break_expr ())
{
auto &break_expr = expr.get_break_expr ();
break_expr->accept_vis (*this);
if (break_expr->is_marked_for_strip ())
rust_error_at (break_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
}
void visit (AST::RangeFromToExpr &expr) override
{
/* outer attributes never allowed before these. while cannot strip
* two direct descendant expressions, can strip ones below that */
/* should have no possibility for outer attrs as would be parsed
* with outer expr */
expr.get_from_expr ()->accept_vis (*this);
/* should syntactically not have outer attributes, though this may
* not have worked in practice */
expr.get_to_expr ()->accept_vis (*this);
// ensure that they are not marked for strip
if (expr.get_from_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_from_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes are never allowed "
"before range exprs");
if (expr.get_to_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_to_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::RangeFromExpr &expr) override
{
/* outer attributes never allowed before these. while cannot strip
* direct descendant expression, can strip ones below that */
/* should have no possibility for outer attrs as would be parsed
* with outer expr */
auto &from_expr = expr.get_from_expr ();
from_expr->accept_vis (*this);
if (from_expr->is_marked_for_strip ())
rust_error_at (from_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes are never allowed before range exprs");
}
void visit (AST::RangeToExpr &expr) override
{
/* outer attributes never allowed before these. while cannot strip
* direct descendant expression, can strip ones below that */
/* should syntactically not have outer attributes, though this may
* not have worked in practice */
auto &to_expr = expr.get_to_expr ();
to_expr->accept_vis (*this);
if (to_expr->is_marked_for_strip ())
rust_error_at (to_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::RangeFullExpr &) override
{
// outer attributes never allowed before these, so no stripping
}
void visit (AST::RangeFromToInclExpr &expr) override
{
/* outer attributes never allowed before these. while cannot strip
* two direct descendant expressions, can strip ones below that */
/* should have no possibility for outer attrs as would be parsed
* with outer expr */
expr.get_from_expr ()->accept_vis (*this);
/* should syntactically not have outer attributes, though this may
* not have worked in practice */
expr.get_to_expr ()->accept_vis (*this);
// ensure that they are not marked for strip
if (expr.get_from_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_from_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes are never allowed "
"before range exprs");
if (expr.get_to_expr ()->is_marked_for_strip ())
rust_error_at (expr.get_to_expr ()->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::RangeToInclExpr &expr) override
{
/* outer attributes never allowed before these. while cannot strip
* direct descendant expression, can strip ones below that */
/* should syntactically not have outer attributes, though this may
* not have worked in practice */
auto &to_expr = expr.get_to_expr ();
to_expr->accept_vis (*this);
if (to_expr->is_marked_for_strip ())
rust_error_at (to_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::ReturnExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* spec does not say that you can have outer attributes on
* expression, so assuming you can't. stripping for sub-expressions
* is the only thing that can be done */
if (expr.has_returned_expr ())
{
auto &returned_expr = expr.get_returned_expr ();
returned_expr->accept_vis (*this);
if (returned_expr->is_marked_for_strip ())
rust_error_at (returned_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
/* TODO: conceptually, you would maybe be able to remove a returned
* expr - e.g. if you had conditional compilation returning void or
* returning a type. On the other hand, I think that function
* return type cannot be conditionally compiled, so I assumed you
* can't do this either. */
}
void visit (AST::UnsafeBlockExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
// can't strip block itself, but can strip sub-expressions
auto &block_expr = expr.get_block_expr ();
block_expr->accept_vis (*this);
if (block_expr->is_marked_for_strip ())
rust_error_at (block_expr->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
}
void visit (AST::LoopExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
// can't strip block itself, but can strip sub-expressions
auto &loop_block = expr.get_loop_block ();
loop_block->accept_vis (*this);
if (loop_block->is_marked_for_strip ())
rust_error_at (loop_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
}
void visit (AST::WhileLoopExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
// can't strip predicate expr itself, but can strip sub-expressions
auto &predicate_expr = expr.get_predicate_expr ();
predicate_expr->accept_vis (*this);
if (predicate_expr->is_marked_for_strip ())
rust_error_at (predicate_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
// can't strip block itself, but can strip sub-expressions
auto &loop_block = expr.get_loop_block ();
loop_block->accept_vis (*this);
if (loop_block->is_marked_for_strip ())
rust_error_at (loop_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
}
void visit (AST::WhileLetLoopExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
for (auto &pattern : expr.get_patterns ())
{
pattern->accept_vis (*this);
if (pattern->is_marked_for_strip ())
rust_error_at (pattern->get_locus (),
"cannot strip pattern in this position");
}
// can't strip scrutinee expr itself, but can strip sub-expressions
auto &scrutinee_expr = expr.get_scrutinee_expr ();
scrutinee_expr->accept_vis (*this);
if (scrutinee_expr->is_marked_for_strip ())
rust_error_at (scrutinee_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
// can't strip block itself, but can strip sub-expressions
auto &loop_block = expr.get_loop_block ();
loop_block->accept_vis (*this);
if (loop_block->is_marked_for_strip ())
rust_error_at (loop_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
}
void visit (AST::ForLoopExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
// strip sub-patterns of pattern
auto &pattern = expr.get_pattern ();
pattern->accept_vis (*this);
if (pattern->is_marked_for_strip ())
rust_error_at (pattern->get_locus (),
"cannot strip pattern in this position");
// can't strip scrutinee expr itself, but can strip sub-expressions
auto &iterator_expr = expr.get_iterator_expr ();
iterator_expr->accept_vis (*this);
if (iterator_expr->is_marked_for_strip ())
rust_error_at (iterator_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
// can't strip block itself, but can strip sub-expressions
auto &loop_block = expr.get_loop_block ();
loop_block->accept_vis (*this);
if (loop_block->is_marked_for_strip ())
rust_error_at (loop_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
}
void visit (AST::IfExpr &expr) override
{
// rust playground test shows that IfExpr does support outer attrs, at least
// when used as statement
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
// can't strip condition expr itself, but can strip sub-expressions
auto &condition_expr = expr.get_condition_expr ();
condition_expr->accept_vis (*this);
if (condition_expr->is_marked_for_strip ())
rust_error_at (condition_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
// can't strip if block itself, but can strip sub-expressions
auto &if_block = expr.get_if_block ();
if_block->accept_vis (*this);
if (if_block->is_marked_for_strip ())
rust_error_at (if_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
}
void visit (AST::IfExprConseqElse &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
// can't strip condition expr itself, but can strip sub-expressions
auto &condition_expr = expr.get_condition_expr ();
condition_expr->accept_vis (*this);
if (condition_expr->is_marked_for_strip ())
rust_error_at (condition_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
// can't strip if block itself, but can strip sub-expressions
auto &if_block = expr.get_if_block ();
if_block->accept_vis (*this);
if (if_block->is_marked_for_strip ())
rust_error_at (if_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
// can't strip else block itself, but can strip sub-expressions
auto &else_block = expr.get_else_block ();
else_block->accept_vis (*this);
if (else_block->is_marked_for_strip ())
rust_error_at (else_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
}
void visit (AST::IfExprConseqIf &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
// can't strip condition expr itself, but can strip sub-expressions
auto &condition_expr = expr.get_condition_expr ();
condition_expr->accept_vis (*this);
if (condition_expr->is_marked_for_strip ())
rust_error_at (condition_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
// can't strip if block itself, but can strip sub-expressions
auto &if_block = expr.get_if_block ();
if_block->accept_vis (*this);
if (if_block->is_marked_for_strip ())
rust_error_at (if_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
// can't strip if expr itself, but can strip sub-expressions
auto &conseq_if_expr = expr.get_conseq_if_expr ();
conseq_if_expr->accept_vis (*this);
if (conseq_if_expr->is_marked_for_strip ())
rust_error_at (conseq_if_expr->get_locus (),
"cannot strip consequent if expression in this "
"position - outer attributes not allowed");
}
void visit (AST::IfExprConseqIfLet &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
// can't strip condition expr itself, but can strip sub-expressions
auto &condition_expr = expr.get_condition_expr ();
condition_expr->accept_vis (*this);
if (condition_expr->is_marked_for_strip ())
rust_error_at (condition_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
// can't strip if block itself, but can strip sub-expressions
auto &if_block = expr.get_if_block ();
if_block->accept_vis (*this);
if (if_block->is_marked_for_strip ())
rust_error_at (if_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
// can't strip if let expr itself, but can strip sub-expressions
auto &conseq_if_let_expr = expr.get_conseq_if_let_expr ();
conseq_if_let_expr->accept_vis (*this);
if (conseq_if_let_expr->is_marked_for_strip ())
rust_error_at (conseq_if_let_expr->get_locus (),
"cannot strip consequent if let expression in this "
"position - outer attributes not "
"allowed");
}
void visit (AST::IfLetExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
for (auto &pattern : expr.get_patterns ())
{
pattern->accept_vis (*this);
if (pattern->is_marked_for_strip ())
rust_error_at (pattern->get_locus (),
"cannot strip pattern in this position");
}
// can't strip value expr itself, but can strip sub-expressions
auto &value_expr = expr.get_value_expr ();
value_expr->accept_vis (*this);
if (value_expr->is_marked_for_strip ())
rust_error_at (value_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
// can't strip if block itself, but can strip sub-expressions
auto &if_block = expr.get_if_block ();
if_block->accept_vis (*this);
if (if_block->is_marked_for_strip ())
rust_error_at (if_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
}
void visit (AST::IfLetExprConseqElse &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
for (auto &pattern : expr.get_patterns ())
{
pattern->accept_vis (*this);
if (pattern->is_marked_for_strip ())
rust_error_at (pattern->get_locus (),
"cannot strip pattern in this position");
}
// can't strip value expr itself, but can strip sub-expressions
auto &value_expr = expr.get_value_expr ();
value_expr->accept_vis (*this);
if (value_expr->is_marked_for_strip ())
rust_error_at (value_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
// can't strip if block itself, but can strip sub-expressions
auto &if_block = expr.get_if_block ();
if_block->accept_vis (*this);
if (if_block->is_marked_for_strip ())
rust_error_at (if_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
// can't strip else block itself, but can strip sub-expressions
auto &else_block = expr.get_else_block ();
else_block->accept_vis (*this);
if (else_block->is_marked_for_strip ())
rust_error_at (else_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
}
void visit (AST::IfLetExprConseqIf &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
for (auto &pattern : expr.get_patterns ())
{
pattern->accept_vis (*this);
if (pattern->is_marked_for_strip ())
rust_error_at (pattern->get_locus (),
"cannot strip pattern in this position");
}
// can't strip value expr itself, but can strip sub-expressions
auto &value_expr = expr.get_value_expr ();
value_expr->accept_vis (*this);
if (value_expr->is_marked_for_strip ())
rust_error_at (value_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
// can't strip if block itself, but can strip sub-expressions
auto &if_block = expr.get_if_block ();
if_block->accept_vis (*this);
if (if_block->is_marked_for_strip ())
rust_error_at (if_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
// can't strip if expr itself, but can strip sub-expressions
auto &conseq_if_expr = expr.get_conseq_if_expr ();
conseq_if_expr->accept_vis (*this);
if (conseq_if_expr->is_marked_for_strip ())
rust_error_at (conseq_if_expr->get_locus (),
"cannot strip consequent if expression in this "
"position - outer attributes not allowed");
}
void visit (AST::IfLetExprConseqIfLet &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
for (auto &pattern : expr.get_patterns ())
{
pattern->accept_vis (*this);
if (pattern->is_marked_for_strip ())
rust_error_at (pattern->get_locus (),
"cannot strip pattern in this position");
}
// can't strip value expr itself, but can strip sub-expressions
auto &value_expr = expr.get_value_expr ();
value_expr->accept_vis (*this);
if (value_expr->is_marked_for_strip ())
rust_error_at (value_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
// can't strip if block itself, but can strip sub-expressions
auto &if_block = expr.get_if_block ();
if_block->accept_vis (*this);
if (if_block->is_marked_for_strip ())
rust_error_at (if_block->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
// can't strip if let expr itself, but can strip sub-expressions
auto &conseq_if_let_expr = expr.get_conseq_if_let_expr ();
conseq_if_let_expr->accept_vis (*this);
if (conseq_if_let_expr->is_marked_for_strip ())
rust_error_at (conseq_if_let_expr->get_locus (),
"cannot strip consequent if let expression in this "
"position - outer attributes not "
"allowed");
}
void visit (AST::MatchExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
// inner attr strip test
expander.expand_cfg_attrs (expr.get_inner_attrs ());
if (expander.fails_cfg_with_expand (expr.get_inner_attrs ()))
{
expr.mark_for_strip ();
return;
}
// can't strip scrutinee expr itself, but can strip sub-expressions
auto &scrutinee_expr = expr.get_scrutinee_expr ();
scrutinee_expr->accept_vis (*this);
if (scrutinee_expr->is_marked_for_strip ())
rust_error_at (scrutinee_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
// strip match cases
auto &match_cases = expr.get_match_cases ();
for (auto it = match_cases.begin (); it != match_cases.end ();)
{
auto &match_case = *it;
// strip match case based on outer attributes in match arm
auto &match_arm = match_case.get_arm ();
expander.expand_cfg_attrs (match_arm.get_outer_attrs ());
if (expander.fails_cfg_with_expand (match_arm.get_outer_attrs ()))
{
// strip match case
it = match_cases.erase (it);
continue;
}
for (auto &pattern : match_arm.get_patterns ())
{
pattern->accept_vis (*this);
if (pattern->is_marked_for_strip ())
rust_error_at (pattern->get_locus (),
"cannot strip pattern in this position");
}
/* assuming that guard expression cannot be stripped as
* strictly speaking you would have to strip the whole guard to
* make syntactical sense, which you can't do. as such, only
* strip sub-expressions */
if (match_arm.has_match_arm_guard ())
{
auto &guard_expr = match_arm.get_guard_expr ();
guard_expr->accept_vis (*this);
if (guard_expr->is_marked_for_strip ())
rust_error_at (guard_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
// strip sub-expressions from match cases
auto &case_expr = match_case.get_expr ();
case_expr->accept_vis (*this);
if (case_expr->is_marked_for_strip ())
rust_error_at (case_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
// increment to next case if haven't continued
++it;
}
}
void visit (AST::AwaitExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
/* can't strip awaited expr itself, but can strip sub-expressions
* - this is because you can't have no expr to await */
auto &awaited_expr = expr.get_awaited_expr ();
awaited_expr->accept_vis (*this);
if (awaited_expr->is_marked_for_strip ())
rust_error_at (awaited_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::AsyncBlockExpr &expr) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (expr.get_outer_attrs ());
if (expander.fails_cfg_with_expand (expr.get_outer_attrs ()))
{
expr.mark_for_strip ();
return;
}
// can't strip block itself, but can strip sub-expressions
auto &block_expr = expr.get_block_expr ();
block_expr->accept_vis (*this);
if (block_expr->is_marked_for_strip ())
rust_error_at (block_expr->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
}
void visit (AST::TypeParam ¶m) override
{
// outer attributes don't actually do anything, so ignore them
if (param.has_type_param_bounds ())
{
// don't strip directly, only components of bounds
for (auto &bound : param.get_type_param_bounds ())
bound->accept_vis (*this);
}
if (param.has_type ())
{
auto &type = param.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (),
"cannot strip type in this position");
}
}
void visit (AST::LifetimeWhereClauseItem &) override
{
// shouldn't require
}
void visit (AST::TypeBoundWhereClauseItem &item) override
{
// for lifetimes shouldn't require
auto &type = item.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (), "cannot strip type in this position");
// don't strip directly, only components of bounds
for (auto &bound : item.get_type_param_bounds ())
bound->accept_vis (*this);
}
void visit (AST::Method &method) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (method.get_outer_attrs ());
if (expander.fails_cfg_with_expand (method.get_outer_attrs ()))
{
method.mark_for_strip ();
return;
}
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : method.get_generic_params ())
param->accept_vis (*this);
/* assuming you can't strip self param - wouldn't be a method
* anymore. spec allows outer attrs on self param, but doesn't
* specify whether cfg is used. */
expand_self_param (method.get_self_param ());
/* strip method parameters if required - this is specifically
* allowed by spec */
expand_function_params (method.get_function_params ());
if (method.has_return_type ())
{
auto &return_type = method.get_return_type ();
return_type->accept_vis (*this);
if (return_type->is_marked_for_strip ())
rust_error_at (return_type->get_locus (),
"cannot strip type in this position");
}
if (method.has_where_clause ())
expand_where_clause (method.get_where_clause ());
/* body should always exist - if error state, should have returned
* before now */
// can't strip block itself, but can strip sub-expressions
auto &block_expr = method.get_definition ();
block_expr->accept_vis (*this);
if (block_expr->is_marked_for_strip ())
rust_error_at (block_expr->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
}
void visit (AST::Module &module) override
{
// strip test based on outer attrs
expander.expand_cfg_attrs (module.get_outer_attrs ());
if (expander.fails_cfg_with_expand (module.get_outer_attrs ()))
{
module.mark_for_strip ();
return;
}
// A loaded module might have inner attributes
if (module.get_kind () == AST::Module::ModuleKind::LOADED)
{
// strip test based on inner attrs
expander.expand_cfg_attrs (module.get_inner_attrs ());
if (expander.fails_cfg_with_expand (module.get_inner_attrs ()))
{
module.mark_for_strip ();
return;
}
}
// Parse the module's items if they haven't been expanded and the file
// should be parsed (i.e isn't hidden behind an untrue or impossible cfg
// directive)
if (!module.is_marked_for_strip ()
&& module.get_kind () == AST::Module::ModuleKind::UNLOADED)
{
module.load_items ();
}
// strip items if required
expand_pointer_allow_strip (module.get_items ());
}
void visit (AST::ExternCrate &crate) override
{
// strip test based on outer attrs
expander.expand_cfg_attrs (crate.get_outer_attrs ());
if (expander.fails_cfg_with_expand (crate.get_outer_attrs ()))
{
crate.mark_for_strip ();
return;
}
}
void visit (AST::UseTreeGlob &) override
{
// shouldn't require?
}
void visit (AST::UseTreeList &) override
{
// shouldn't require?
}
void visit (AST::UseTreeRebind &) override
{
// shouldn't require?
}
void visit (AST::UseDeclaration &use_decl) override
{
// strip test based on outer attrs
expander.expand_cfg_attrs (use_decl.get_outer_attrs ());
if (expander.fails_cfg_with_expand (use_decl.get_outer_attrs ()))
{
use_decl.mark_for_strip ();
return;
}
}
void visit (AST::Function &function) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (function.get_outer_attrs ());
if (expander.fails_cfg_with_expand (function.get_outer_attrs ()))
{
function.mark_for_strip ();
return;
}
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : function.get_generic_params ())
param->accept_vis (*this);
/* strip function parameters if required - this is specifically
* allowed by spec */
expand_function_params (function.get_function_params ());
if (function.has_return_type ())
{
auto &return_type = function.get_return_type ();
return_type->accept_vis (*this);
if (return_type->is_marked_for_strip ())
rust_error_at (return_type->get_locus (),
"cannot strip type in this position");
}
if (function.has_where_clause ())
expand_where_clause (function.get_where_clause ());
/* body should always exist - if error state, should have returned
* before now */
// can't strip block itself, but can strip sub-expressions
auto &block_expr = function.get_definition ();
block_expr->accept_vis (*this);
if (block_expr->is_marked_for_strip ())
rust_error_at (block_expr->get_locus (),
"cannot strip block expression in this position - outer "
"attributes not allowed");
}
void visit (AST::TypeAlias &type_alias) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (type_alias.get_outer_attrs ());
if (expander.fails_cfg_with_expand (type_alias.get_outer_attrs ()))
{
type_alias.mark_for_strip ();
return;
}
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : type_alias.get_generic_params ())
param->accept_vis (*this);
if (type_alias.has_where_clause ())
expand_where_clause (type_alias.get_where_clause ());
auto &type = type_alias.get_type_aliased ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (), "cannot strip type in this position");
}
void visit (AST::StructStruct &struct_item) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (struct_item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (struct_item.get_outer_attrs ()))
{
struct_item.mark_for_strip ();
return;
}
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : struct_item.get_generic_params ())
param->accept_vis (*this);
if (struct_item.has_where_clause ())
expand_where_clause (struct_item.get_where_clause ());
/* strip struct fields if required - this is presumably
* allowed by spec */
expand_struct_fields (struct_item.get_fields ());
}
void visit (AST::TupleStruct &tuple_struct) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (tuple_struct.get_outer_attrs ());
if (expander.fails_cfg_with_expand (tuple_struct.get_outer_attrs ()))
{
tuple_struct.mark_for_strip ();
return;
}
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : tuple_struct.get_generic_params ())
param->accept_vis (*this);
/* strip struct fields if required - this is presumably
* allowed by spec */
expand_tuple_fields (tuple_struct.get_fields ());
if (tuple_struct.has_where_clause ())
expand_where_clause (tuple_struct.get_where_clause ());
}
void visit (AST::EnumItem &item) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
{
item.mark_for_strip ();
return;
}
}
void visit (AST::EnumItemTuple &item) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
{
item.mark_for_strip ();
return;
}
/* strip item fields if required - this is presumably
* allowed by spec */
expand_tuple_fields (item.get_tuple_fields ());
}
void visit (AST::EnumItemStruct &item) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
{
item.mark_for_strip ();
return;
}
/* strip item fields if required - this is presumably
* allowed by spec */
expand_struct_fields (item.get_struct_fields ());
}
void visit (AST::EnumItemDiscriminant &item) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
{
item.mark_for_strip ();
return;
}
/* strip any internal sub-expressions - expression itself isn't
* allowed to have external attributes in this position so can't be
* stripped. */
auto &expr = item.get_expr ();
expr->accept_vis (*this);
if (expr->is_marked_for_strip ())
rust_error_at (expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::Enum &enum_item) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (enum_item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (enum_item.get_outer_attrs ()))
{
enum_item.mark_for_strip ();
return;
}
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : enum_item.get_generic_params ())
param->accept_vis (*this);
if (enum_item.has_where_clause ())
expand_where_clause (enum_item.get_where_clause ());
/* strip enum fields if required - this is presumably
* allowed by spec */
expand_pointer_allow_strip (enum_item.get_variants ());
}
void visit (AST::Union &union_item) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (union_item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (union_item.get_outer_attrs ()))
{
union_item.mark_for_strip ();
return;
}
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : union_item.get_generic_params ())
param->accept_vis (*this);
if (union_item.has_where_clause ())
expand_where_clause (union_item.get_where_clause ());
/* strip union fields if required - this is presumably
* allowed by spec */
expand_struct_fields (union_item.get_variants ());
}
void visit (AST::ConstantItem &const_item) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (const_item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (const_item.get_outer_attrs ()))
{
const_item.mark_for_strip ();
return;
}
// strip any sub-types
auto &type = const_item.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (), "cannot strip type in this position");
/* strip any internal sub-expressions - expression itself isn't
* allowed to have external attributes in this position so can't be
* stripped. */
auto &expr = const_item.get_expr ();
expr->accept_vis (*this);
if (expr->is_marked_for_strip ())
rust_error_at (expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::StaticItem &static_item) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (static_item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (static_item.get_outer_attrs ()))
{
static_item.mark_for_strip ();
return;
}
// strip any sub-types
auto &type = static_item.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (), "cannot strip type in this position");
/* strip any internal sub-expressions - expression itself isn't
* allowed to have external attributes in this position so can't be
* stripped. */
auto &expr = static_item.get_expr ();
expr->accept_vis (*this);
if (expr->is_marked_for_strip ())
rust_error_at (expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
void visit (AST::TraitItemFunc &item) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
{
item.mark_for_strip ();
return;
}
expand_trait_function_decl (item.get_trait_function_decl ());
if (item.has_definition ())
{
/* strip any internal sub-expressions - expression itself isn't
* allowed to have external attributes in this position so can't be
* stripped. */
auto &block = item.get_definition ();
block->accept_vis (*this);
if (block->is_marked_for_strip ())
rust_error_at (block->get_locus (),
"cannot strip block expression in this "
"position - outer attributes not allowed");
}
}
void visit (AST::TraitItemMethod &item) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
{
item.mark_for_strip ();
return;
}
expand_trait_method_decl (item.get_trait_method_decl ());
if (item.has_definition ())
{
/* strip any internal sub-expressions - expression itself isn't
* allowed to have external attributes in this position so can't be
* stripped. */
auto &block = item.get_definition ();
block->accept_vis (*this);
if (block->is_marked_for_strip ())
rust_error_at (block->get_locus (),
"cannot strip block expression in this "
"position - outer attributes not allowed");
}
}
void visit (AST::TraitItemConst &item) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
{
item.mark_for_strip ();
return;
}
// strip any sub-types
auto &type = item.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (), "cannot strip type in this position");
/* strip any internal sub-expressions - expression itself isn't
* allowed to have external attributes in this position so can't be
* stripped */
if (item.has_expression ())
{
auto &expr = item.get_expr ();
expr->accept_vis (*this);
if (expr->is_marked_for_strip ())
rust_error_at (expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
}
void visit (AST::TraitItemType &item) override
{
// initial test based on outer attrs
expander.expand_cfg_attrs (item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
{
item.mark_for_strip ();
return;
}
if (item.has_type_param_bounds ())
{
// don't strip directly, only components of bounds
for (auto &bound : item.get_type_param_bounds ())
bound->accept_vis (*this);
}
}
void visit (AST::Trait &trait) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (trait.get_outer_attrs ());
if (expander.fails_cfg_with_expand (trait.get_outer_attrs ()))
{
trait.mark_for_strip ();
return;
}
// strip test based on inner attrs
expander.expand_cfg_attrs (trait.get_inner_attrs ());
if (expander.fails_cfg_with_expand (trait.get_inner_attrs ()))
{
trait.mark_for_strip ();
return;
}
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : trait.get_generic_params ())
param->accept_vis (*this);
if (trait.has_type_param_bounds ())
{
// don't strip directly, only components of bounds
for (auto &bound : trait.get_type_param_bounds ())
bound->accept_vis (*this);
}
if (trait.has_where_clause ())
expand_where_clause (trait.get_where_clause ());
// strip trait items if required
expand_pointer_allow_strip (trait.get_trait_items ());
}
void visit (AST::InherentImpl &impl) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (impl.get_outer_attrs ());
if (expander.fails_cfg_with_expand (impl.get_outer_attrs ()))
{
impl.mark_for_strip ();
return;
}
// strip test based on inner attrs
expander.expand_cfg_attrs (impl.get_inner_attrs ());
if (expander.fails_cfg_with_expand (impl.get_inner_attrs ()))
{
impl.mark_for_strip ();
return;
}
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : impl.get_generic_params ())
param->accept_vis (*this);
auto &type = impl.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (), "cannot strip type in this position");
if (impl.has_where_clause ())
expand_where_clause (impl.get_where_clause ());
// strip inherent impl items if required
expand_pointer_allow_strip (impl.get_impl_items ());
}
void visit (AST::TraitImpl &impl) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (impl.get_outer_attrs ());
if (expander.fails_cfg_with_expand (impl.get_outer_attrs ()))
{
impl.mark_for_strip ();
return;
}
// strip test based on inner attrs
expander.expand_cfg_attrs (impl.get_inner_attrs ());
if (expander.fails_cfg_with_expand (impl.get_inner_attrs ()))
{
impl.mark_for_strip ();
return;
}
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : impl.get_generic_params ())
param->accept_vis (*this);
auto &type = impl.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (), "cannot strip type in this position");
auto &trait_path = impl.get_trait_path ();
visit (trait_path);
if (trait_path.is_marked_for_strip ())
rust_error_at (trait_path.get_locus (),
"cannot strip typepath in this position");
if (impl.has_where_clause ())
expand_where_clause (impl.get_where_clause ());
// strip trait impl items if required
expand_pointer_allow_strip (impl.get_impl_items ());
}
void visit (AST::ExternalStaticItem &item) override
{
// strip test based on outer attrs
expander.expand_cfg_attrs (item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
{
item.mark_for_strip ();
return;
}
auto &type = item.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (), "cannot strip type in this position");
}
void visit (AST::ExternalFunctionItem &item) override
{
// strip test based on outer attrs
expander.expand_cfg_attrs (item.get_outer_attrs ());
if (expander.fails_cfg_with_expand (item.get_outer_attrs ()))
{
item.mark_for_strip ();
return;
}
// just expand sub-stuff - can't actually strip generic params themselves
for (auto ¶m : item.get_generic_params ())
param->accept_vis (*this);
/* strip function parameters if required - this is specifically
* allowed by spec */
auto ¶ms = item.get_function_params ();
for (auto it = params.begin (); it != params.end ();)
{
auto ¶m = *it;
auto ¶m_attrs = param.get_outer_attrs ();
expander.expand_cfg_attrs (param_attrs);
if (expander.fails_cfg_with_expand (param_attrs))
{
it = params.erase (it);
continue;
}
auto &type = param.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (),
"cannot strip type in this position");
// increment if nothing else happens
++it;
}
/* NOTE: these are extern function params, which may have different
* rules and restrictions to "normal" function params. So expansion
* handled separately. */
/* TODO: assuming that variadic nature cannot be stripped. If this
* is not true, then have code here to do so. */
if (item.has_return_type ())
{
auto &return_type = item.get_return_type ();
return_type->accept_vis (*this);
if (return_type->is_marked_for_strip ())
rust_error_at (return_type->get_locus (),
"cannot strip type in this position");
}
if (item.has_where_clause ())
expand_where_clause (item.get_where_clause ());
}
void visit (AST::ExternBlock &block) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (block.get_outer_attrs ());
if (expander.fails_cfg_with_expand (block.get_outer_attrs ()))
{
block.mark_for_strip ();
return;
}
// strip test based on inner attrs
expander.expand_cfg_attrs (block.get_inner_attrs ());
if (expander.fails_cfg_with_expand (block.get_inner_attrs ()))
{
block.mark_for_strip ();
return;
}
// strip external items if required
expand_pointer_allow_strip (block.get_extern_items ());
}
// I don't think it would be possible to strip macros without expansion
void visit (AST::MacroMatchFragment &) override {}
void visit (AST::MacroMatchRepetition &) override {}
void visit (AST::MacroMatcher &) override {}
void visit (AST::MacroRulesDefinition &rules_def) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (rules_def.get_outer_attrs ());
if (expander.fails_cfg_with_expand (rules_def.get_outer_attrs ()))
{
rules_def.mark_for_strip ();
return;
}
// I don't think any macro rules can be stripped in any way
auto path = Resolver::CanonicalPath::new_seg (rules_def.get_node_id (),
rules_def.get_rule_name ());
expander.resolver->get_macro_scope ().insert (path,
rules_def.get_node_id (),
rules_def.get_locus ());
expander.mappings->insert_macro_def (&rules_def);
}
void visit (AST::MacroInvocation ¯o_invoc) override
{
// FIXME
// we probably need another recurision check here
// initial strip test based on outer attrs
expander.expand_cfg_attrs (macro_invoc.get_outer_attrs ());
if (expander.fails_cfg_with_expand (macro_invoc.get_outer_attrs ()))
{
macro_invoc.mark_for_strip ();
return;
}
// I don't think any macro token trees can be stripped in any way
expander.expand_invoc (macro_invoc);
// we need to visit the expanded fragments since it may need cfg expansion
// and it may be recursive
for (auto &node : macro_invoc.get_fragment ().get_nodes ())
node.accept_vis (*this);
}
void visit (AST::MetaItemPath &) override {}
void visit (AST::MetaItemSeq &) override {}
void visit (AST::MetaWord &) override {}
void visit (AST::MetaNameValueStr &) override {}
void visit (AST::MetaListPaths &) override {}
void visit (AST::MetaListNameValueStr &) override {}
void visit (AST::LiteralPattern &) override
{
// not possible
}
void visit (AST::IdentifierPattern &pattern) override
{
// can only strip sub-patterns of the inner pattern to bind
if (!pattern.has_pattern_to_bind ())
return;
auto &sub_pattern = pattern.get_pattern_to_bind ();
sub_pattern->accept_vis (*this);
if (sub_pattern->is_marked_for_strip ())
rust_error_at (sub_pattern->get_locus (),
"cannot strip pattern in this position");
}
void visit (AST::WildcardPattern &) override
{
// not possible
}
void visit (AST::RangePatternBoundLiteral &) override
{
// not possible
}
void visit (AST::RangePatternBoundPath &bound) override
{
// can expand path, but not strip it directly
auto &path = bound.get_path ();
visit (path);
if (path.is_marked_for_strip ())
rust_error_at (path.get_locus (), "cannot strip path in this position");
}
void visit (AST::RangePatternBoundQualPath &bound) override
{
// can expand path, but not strip it directly
auto &path = bound.get_qualified_path ();
visit (path);
if (path.is_marked_for_strip ())
rust_error_at (path.get_locus (), "cannot strip path in this position");
}
void visit (AST::RangePattern &pattern) override
{
// should have no capability to strip lower or upper bounds, only expand
pattern.get_lower_bound ()->accept_vis (*this);
pattern.get_upper_bound ()->accept_vis (*this);
}
void visit (AST::ReferencePattern &pattern) override
{
auto &sub_pattern = pattern.get_referenced_pattern ();
sub_pattern->accept_vis (*this);
if (sub_pattern->is_marked_for_strip ())
rust_error_at (sub_pattern->get_locus (),
"cannot strip pattern in this position");
}
void visit (AST::StructPatternFieldTuplePat &field) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (field.get_outer_attrs ());
if (expander.fails_cfg_with_expand (field.get_outer_attrs ()))
{
field.mark_for_strip ();
return;
}
// strip sub-patterns (can't strip top-level pattern)
auto &sub_pattern = field.get_index_pattern ();
sub_pattern->accept_vis (*this);
if (sub_pattern->is_marked_for_strip ())
rust_error_at (sub_pattern->get_locus (),
"cannot strip pattern in this position");
}
void visit (AST::StructPatternFieldIdentPat &field) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (field.get_outer_attrs ());
if (expander.fails_cfg_with_expand (field.get_outer_attrs ()))
{
field.mark_for_strip ();
return;
}
// strip sub-patterns (can't strip top-level pattern)
auto &sub_pattern = field.get_ident_pattern ();
sub_pattern->accept_vis (*this);
if (sub_pattern->is_marked_for_strip ())
rust_error_at (sub_pattern->get_locus (),
"cannot strip pattern in this position");
}
void visit (AST::StructPatternFieldIdent &field) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (field.get_outer_attrs ());
if (expander.fails_cfg_with_expand (field.get_outer_attrs ()))
{
field.mark_for_strip ();
return;
}
}
void visit (AST::StructPattern &pattern) override
{
// expand (but don't strip) path
auto &path = pattern.get_path ();
visit (path);
if (path.is_marked_for_strip ())
rust_error_at (path.get_locus (), "cannot strip path in this position");
/* TODO: apparently struct pattern fields can have outer attrs. so can they
* be stripped? */
if (!pattern.has_struct_pattern_elems ())
return;
auto &elems = pattern.get_struct_pattern_elems ();
// assuming you can strip struct pattern fields
expand_pointer_allow_strip (elems.get_struct_pattern_fields ());
// assuming you can strip the ".." part
if (elems.has_etc ())
{
expander.expand_cfg_attrs (elems.get_etc_outer_attrs ());
if (expander.fails_cfg_with_expand (elems.get_etc_outer_attrs ()))
elems.strip_etc ();
}
}
void visit (AST::TupleStructItemsNoRange &tuple_items) override
{
// can't strip individual patterns, only sub-patterns
for (auto &pattern : tuple_items.get_patterns ())
{
pattern->accept_vis (*this);
if (pattern->is_marked_for_strip ())
rust_error_at (pattern->get_locus (),
"cannot strip pattern in this position");
// TODO: quit stripping now? or keep going?
}
}
void visit (AST::TupleStructItemsRange &tuple_items) override
{
// can't strip individual patterns, only sub-patterns
for (auto &lower_pattern : tuple_items.get_lower_patterns ())
{
lower_pattern->accept_vis (*this);
if (lower_pattern->is_marked_for_strip ())
rust_error_at (lower_pattern->get_locus (),
"cannot strip pattern in this position");
// TODO: quit stripping now? or keep going?
}
for (auto &upper_pattern : tuple_items.get_upper_patterns ())
{
upper_pattern->accept_vis (*this);
if (upper_pattern->is_marked_for_strip ())
rust_error_at (upper_pattern->get_locus (),
"cannot strip pattern in this position");
// TODO: quit stripping now? or keep going?
}
}
void visit (AST::TupleStructPattern &pattern) override
{
// expand (but don't strip) path
auto &path = pattern.get_path ();
visit (path);
if (path.is_marked_for_strip ())
rust_error_at (path.get_locus (), "cannot strip path in this position");
if (pattern.has_items ())
pattern.get_items ()->accept_vis (*this);
}
void visit (AST::TuplePatternItemsMultiple &tuple_items) override
{
// can't strip individual patterns, only sub-patterns
for (auto &pattern : tuple_items.get_patterns ())
{
pattern->accept_vis (*this);
if (pattern->is_marked_for_strip ())
rust_error_at (pattern->get_locus (),
"cannot strip pattern in this position");
// TODO: quit stripping now? or keep going?
}
}
void visit (AST::TuplePatternItemsRanged &tuple_items) override
{
// can't strip individual patterns, only sub-patterns
for (auto &lower_pattern : tuple_items.get_lower_patterns ())
{
lower_pattern->accept_vis (*this);
if (lower_pattern->is_marked_for_strip ())
rust_error_at (lower_pattern->get_locus (),
"cannot strip pattern in this position");
// TODO: quit stripping now? or keep going?
}
for (auto &upper_pattern : tuple_items.get_upper_patterns ())
{
upper_pattern->accept_vis (*this);
if (upper_pattern->is_marked_for_strip ())
rust_error_at (upper_pattern->get_locus (),
"cannot strip pattern in this position");
// TODO: quit stripping now? or keep going?
}
}
void visit (AST::TuplePattern &pattern) override
{
if (pattern.has_tuple_pattern_items ())
pattern.get_items ()->accept_vis (*this);
}
void visit (AST::GroupedPattern &pattern) override
{
// can't strip inner pattern, only sub-patterns
auto &pattern_in_parens = pattern.get_pattern_in_parens ();
pattern_in_parens->accept_vis (*this);
if (pattern_in_parens->is_marked_for_strip ())
rust_error_at (pattern_in_parens->get_locus (),
"cannot strip pattern in this position");
}
void visit (AST::SlicePattern &pattern) override
{
// can't strip individual patterns, only sub-patterns
for (auto &item : pattern.get_items ())
{
item->accept_vis (*this);
if (item->is_marked_for_strip ())
rust_error_at (item->get_locus (),
"cannot strip pattern in this position");
// TODO: quit stripping now? or keep going?
}
}
void visit (AST::EmptyStmt &) override
{
// assuming no outer attributes, so nothing can happen
}
void visit (AST::LetStmt &stmt) override
{
// initial strip test based on outer attrs
expander.expand_cfg_attrs (stmt.get_outer_attrs ());
if (expander.fails_cfg_with_expand (stmt.get_outer_attrs ()))
{
stmt.mark_for_strip ();
return;
}
// can't strip pattern, but call for sub-patterns
auto &pattern = stmt.get_pattern ();
pattern->accept_vis (*this);
if (pattern->is_marked_for_strip ())
rust_error_at (pattern->get_locus (),
"cannot strip pattern in this position");
// similar for type
if (stmt.has_type ())
{
auto &type = stmt.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (),
"cannot strip type in this position");
}
/* strip any internal sub-expressions - expression itself isn't
* allowed to have external attributes in this position so can't be
* stripped */
if (stmt.has_init_expr ())
{
auto &init_expr = stmt.get_init_expr ();
init_expr->accept_vis (*this);
if (init_expr->is_marked_for_strip ())
rust_error_at (init_expr->get_locus (),
"cannot strip expression in this position - outer "
"attributes not allowed");
}
}
void visit (AST::ExprStmtWithoutBlock &stmt) override
{
// outer attributes associated with expr, so rely on expr
// guard - should prevent null pointer expr
if (stmt.is_marked_for_strip ())
return;
// strip if expr is to be stripped
auto &expr = stmt.get_expr ();
expr->accept_vis (*this);
if (expr->is_marked_for_strip ())
{
stmt.mark_for_strip ();
return;
}
}
void visit (AST::ExprStmtWithBlock &stmt) override
{
// outer attributes associated with expr, so rely on expr
// guard - should prevent null pointer expr
if (stmt.is_marked_for_strip ())
return;
// strip if expr is to be stripped
auto &expr = stmt.get_expr ();
expr->accept_vis (*this);
if (expr->is_marked_for_strip ())
{
stmt.mark_for_strip ();
return;
}
}
void visit (AST::TraitBound &bound) override
{
// nothing in for lifetimes to strip
// expand but don't strip type path
auto &path = bound.get_type_path ();
visit (path);
if (path.is_marked_for_strip ())
rust_error_at (path.get_locus (),
"cannot strip type path in this position");
}
void visit (AST::ImplTraitType &type) override
{
// don't strip directly, only components of bounds
for (auto &bound : type.get_type_param_bounds ())
bound->accept_vis (*this);
}
void visit (AST::TraitObjectType &type) override
{
// don't strip directly, only components of bounds
for (auto &bound : type.get_type_param_bounds ())
bound->accept_vis (*this);
}
void visit (AST::ParenthesisedType &type) override
{
// expand but don't strip inner type
auto &inner_type = type.get_type_in_parens ();
inner_type->accept_vis (*this);
if (inner_type->is_marked_for_strip ())
rust_error_at (inner_type->get_locus (),
"cannot strip type in this position");
}
void visit (AST::ImplTraitTypeOneBound &type) override
{
// no stripping possible
visit (type.get_trait_bound ());
}
void visit (AST::TraitObjectTypeOneBound &type) override
{
// no stripping possible
visit (type.get_trait_bound ());
}
void visit (AST::TupleType &type) override
{
// TODO: assuming that types can't be stripped as types don't have outer
// attributes
for (auto &elem_type : type.get_elems ())
{
elem_type->accept_vis (*this);
if (elem_type->is_marked_for_strip ())
rust_error_at (elem_type->get_locus (),
"cannot strip type in this position");
}
}
void visit (AST::NeverType &) override
{
// no stripping possible
}
void visit (AST::RawPointerType &type) override
{
// expand but don't strip type pointed to
auto &pointed_type = type.get_type_pointed_to ();
pointed_type->accept_vis (*this);
if (pointed_type->is_marked_for_strip ())
rust_error_at (pointed_type->get_locus (),
"cannot strip type in this position");
}
void visit (AST::ReferenceType &type) override
{
// expand but don't strip type referenced
auto &referenced_type = type.get_type_referenced ();
referenced_type->accept_vis (*this);
if (referenced_type->is_marked_for_strip ())
rust_error_at (referenced_type->get_locus (),
"cannot strip type in this position");
}
void visit (AST::ArrayType &type) override
{
// expand but don't strip type referenced
auto &base_type = type.get_elem_type ();
base_type->accept_vis (*this);
if (base_type->is_marked_for_strip ())
rust_error_at (base_type->get_locus (),
"cannot strip type in this position");
// same for expression
auto &size_expr = type.get_size_expr ();
size_expr->accept_vis (*this);
if (size_expr->is_marked_for_strip ())
rust_error_at (size_expr->get_locus (),
"cannot strip expression in this position");
}
void visit (AST::SliceType &type) override
{
// expand but don't strip elem type
auto &elem_type = type.get_elem_type ();
elem_type->accept_vis (*this);
if (elem_type->is_marked_for_strip ())
rust_error_at (elem_type->get_locus (),
"cannot strip type in this position");
}
void visit (AST::InferredType &) override
{
// none possible
}
void visit (AST::BareFunctionType &type) override
{
// seem to be no generics
// presumably function params can be stripped
auto ¶ms = type.get_function_params ();
for (auto it = params.begin (); it != params.end ();)
{
auto ¶m = *it;
auto ¶m_attrs = param.get_outer_attrs ();
expander.expand_cfg_attrs (param_attrs);
if (expander.fails_cfg_with_expand (param_attrs))
{
it = params.erase (it);
continue;
}
auto &type = param.get_type ();
type->accept_vis (*this);
if (type->is_marked_for_strip ())
rust_error_at (type->get_locus (),
"cannot strip type in this position");
// increment if nothing else happens
++it;
}
/* TODO: assuming that variadic nature cannot be stripped. If this
* is not true, then have code here to do so. */
if (type.has_return_type ())
{
auto &return_type = type.get_return_type ();
return_type->accept_vis (*this);
if (return_type->is_marked_for_strip ())
rust_error_at (return_type->get_locus (),
"cannot strip type in this position");
}
// no where clause, apparently
}
};
void
MacroExpander::parse_macro_to_meta_item (AST::MacroInvocData &invoc)
{
// only parse if not already parsed
if (invoc.is_parsed ())
return;
std::unique_ptr converted_input (
invoc.get_delim_tok_tree ().parse_to_meta_item ());
if (converted_input == nullptr)
{
rust_debug ("DEBUG: failed to parse macro to meta item");
// TODO: do something now? is this an actual error?
}
else
{
std::vector> meta_items (
std::move (converted_input->get_items ()));
invoc.set_meta_item_output (std::move (meta_items));
}
}
AST::Literal
MacroExpander::expand_cfg_macro (AST::MacroInvocData &invoc)
{
// only allow on cfg macros
if (invoc.get_path () != "cfg")
return AST::Literal::create_error ();
parse_macro_to_meta_item (invoc);
/* TODO: assuming that cfg! macros can only have one meta item inner, like cfg
* attributes */
if (invoc.get_meta_items ().size () != 1)
return AST::Literal::create_error ();
bool result = invoc.get_meta_items ()[0]->check_cfg_predicate (session);
if (result)
return AST::Literal ("true", AST::Literal::BOOL, CORETYPE_BOOL);
else
return AST::Literal ("false", AST::Literal::BOOL, CORETYPE_BOOL);
}
AST::ASTFragment
MacroExpander::expand_decl_macro (Location invoc_locus,
AST::MacroInvocData &invoc,
AST::MacroRulesDefinition &rules_def,
bool semicolon)
{
// ensure that both invocation and rules are in a valid state
rust_assert (!invoc.is_marked_for_strip ());
rust_assert (!rules_def.is_marked_for_strip ());
rust_assert (rules_def.get_macro_rules ().size () > 0);
/* probably something here about parsing invoc and rules def token trees to
* token stream. if not, how would parser handle the captures of exprs and
* stuff? on the other hand, token trees may be kind of useful in rules def as
* creating a point where recursion can occur (like having
* "compare_macro_match" and then it calling itself when it finds delimiters)
*/
/* find matching rule to invoc token tree, based on macro rule's matcher. if
* none exist, error.
* - specifically, check each matcher in order. if one fails to match, move
* onto next. */
/* TODO: does doing this require parsing expressions and whatever in the
* invoc? if so, might as well save the results if referenced using $ or
* whatever. If not, do another pass saving them. Except this is probably
* useless as different rules could have different starting points for exprs
* or whatever. Decision trees could avoid this, but they have their own
* issues. */
/* TODO: will need to modify the parser so that it can essentially "catch"
* errors - maybe "try_parse_expr" or whatever methods. */
// this technically creates a back-tracking parser - this will be the
// implementation style
/* then, after results are saved, generate the macro output from the
* transcriber token tree. if i understand this correctly, the macro
* invocation gets replaced by the transcriber tokens, except with
* substitutions made (e.g. for $i variables) */
/* TODO: it is probably better to modify AST::Token to store a pointer to a
* Lexer::Token (rather than being converted) - i.e. not so much have
* AST::Token as a Token but rather a TokenContainer (as it is another type of
* TokenTree). This will prevent re-conversion of Tokens between each type
* all the time, while still allowing the heterogenous storage of token trees.
*/
AST::DelimTokenTree &invoc_token_tree = invoc.get_delim_tok_tree ();
// find matching arm
AST::MacroRule *matched_rule = nullptr;
std::map> matched_fragments;
for (auto &rule : rules_def.get_rules ())
{
sub_stack.push ();
bool did_match_rule = try_match_rule (rule, invoc_token_tree);
matched_fragments = sub_stack.pop ();
if (did_match_rule)
{
for (auto &kv : matched_fragments)
rust_debug ("[fragment]: %s (%ld)", kv.first.c_str (),
kv.second.size ());
matched_rule = &rule;
break;
}
}
if (matched_rule == nullptr)
{
RichLocation r (invoc_locus);
r.add_range (rules_def.get_locus ());
rust_error_at (r, "Failed to match any rule within macro");
return AST::ASTFragment::create_empty ();
}
return transcribe_rule (*matched_rule, invoc_token_tree, matched_fragments,
semicolon, peek_context ());
}
void
MacroExpander::expand_invoc (AST::MacroInvocation &invoc)
{
if (depth_exceeds_recursion_limit ())
{
rust_error_at (invoc.get_locus (), "reached recursion limit");
return;
}
AST::MacroInvocData &invoc_data = invoc.get_invoc_data ();
// ??
// switch on type of macro:
// - '!' syntax macro (inner switch)
// - procedural macro - "A token-based function-like macro"
// - 'macro_rules' (by example/pattern-match) macro? or not? "an
// AST-based function-like macro"
// - else is unreachable
// - attribute syntax macro (inner switch)
// - procedural macro attribute syntax - "A token-based attribute
// macro"
// - legacy macro attribute syntax? - "an AST-based attribute macro"
// - non-macro attribute: mark known
// - else is unreachable
// - derive macro (inner switch)
// - derive or legacy derive - "token-based" vs "AST-based"
// - else is unreachable
// - derive container macro - unreachable
// lookup the rules for this macro
NodeId resolved_node = UNKNOWN_NODEID;
bool found = resolver->get_macro_scope ().lookup (
Resolver::CanonicalPath::new_seg (invoc.get_pattern_node_id (),
invoc_data.get_path ().as_string ()),
&resolved_node);
if (!found)
{
rust_error_at (invoc.get_locus (), "unknown macro");
return;
}
// lookup the rules
AST::MacroRulesDefinition *rules_def = nullptr;
bool ok = mappings->lookup_macro_def (resolved_node, &rules_def);
rust_assert (ok);
auto fragment
= expand_decl_macro (invoc.get_locus (), invoc_data, *rules_def, false);
// lets attach this fragment to the invocation
invoc.set_fragment (std::move (fragment));
}
void
MacroExpander::expand_invoc_semi (AST::MacroInvocationSemi &invoc)
{
if (depth_exceeds_recursion_limit ())
{
rust_error_at (invoc.get_locus (), "reached recursion limit");
return;
}
AST::MacroInvocData &invoc_data = invoc.get_invoc_data ();
// lookup the rules for this macro
NodeId resolved_node = UNKNOWN_NODEID;
bool found = resolver->get_macro_scope ().lookup (
Resolver::CanonicalPath::new_seg (invoc.get_macro_node_id (),
invoc_data.get_path ().as_string ()),
&resolved_node);
if (!found)
{
rust_error_at (invoc.get_locus (), "unknown macro");
return;
}
// lookup the rules
AST::MacroRulesDefinition *rules_def = nullptr;
bool ok = mappings->lookup_macro_def (resolved_node, &rules_def);
rust_assert (ok);
auto fragment
= expand_decl_macro (invoc.get_locus (), invoc_data, *rules_def, true);
// lets attach this fragment to the invocation
invoc.set_fragment (std::move (fragment));
}
/* Determines whether any cfg predicate is false and hence item with attributes
* should be stripped. Note that attributes must be expanded before calling. */
bool
MacroExpander::fails_cfg (const AST::AttrVec &attrs) const
{
for (const auto &attr : attrs)
{
if (attr.get_path () == "cfg" && !attr.check_cfg_predicate (session))
return true;
}
return false;
}
/* Determines whether any cfg predicate is false and hence item with attributes
* should be stripped. Will expand attributes as well. */
bool
MacroExpander::fails_cfg_with_expand (AST::AttrVec &attrs) const
{
// TODO: maybe have something that strips cfg attributes that evaluate true?
for (auto &attr : attrs)
{
if (attr.get_path () == "cfg")
{
if (!attr.is_parsed_to_meta_item ())
attr.parse_attr_to_meta_item ();
// DEBUG
if (!attr.is_parsed_to_meta_item ())
rust_debug ("failed to parse attr to meta item, right before "
"cfg predicate check");
else
rust_debug ("attr has been successfully parsed to meta item, "
"right before cfg predicate check");
if (!attr.check_cfg_predicate (session))
{
// DEBUG
rust_debug (
"cfg predicate failed for attribute: \033[0;31m'%s'\033[0m",
attr.as_string ().c_str ());
return true;
}
else
{
// DEBUG
rust_debug ("cfg predicate succeeded for attribute: "
"\033[0;31m'%s'\033[0m",
attr.as_string ().c_str ());
}
}
}
return false;
}
// Expands cfg_attr attributes.
void
MacroExpander::expand_cfg_attrs (AST::AttrVec &attrs)
{
for (std::size_t i = 0; i < attrs.size (); i++)
{
auto &attr = attrs[i];
if (attr.get_path () == "cfg_attr")
{
if (!attr.is_parsed_to_meta_item ())
attr.parse_attr_to_meta_item ();
if (attr.check_cfg_predicate (session))
{
// split off cfg_attr
AST::AttrVec new_attrs = attr.separate_cfg_attrs ();
// remove attr from vector
attrs.erase (attrs.begin () + i);
// add new attrs to vector
attrs.insert (attrs.begin () + i,
std::make_move_iterator (new_attrs.begin ()),
std::make_move_iterator (new_attrs.end ()));
}
/* do something - if feature (first token in tree) is in fact enabled,
* make tokens listed afterwards into attributes. i.e.: for
* [cfg_attr(feature = "wow", wow1, wow2)], if "wow" is true, then add
* attributes [wow1] and [wow2] to attribute list. This can also be
* recursive, so check for expanded attributes being recursive and
* possibly recursively call the expand_attrs? */
}
else
{
i++;
}
}
attrs.shrink_to_fit ();
}
void
MacroExpander::expand_crate ()
{
NodeId scope_node_id = crate.get_node_id ();
resolver->get_macro_scope ().push (scope_node_id);
/* fill macro/decorator map from init list? not sure where init list comes
* from? */
// TODO: does cfg apply for inner attributes? research.
// the apparent answer (from playground test) is yes
// expand crate cfg_attr attributes
expand_cfg_attrs (crate.inner_attrs);
if (fails_cfg_with_expand (crate.inner_attrs))
{
// basically, delete whole crate
crate.strip_crate ();
// TODO: maybe create warning here? probably not desired behaviour
}
// expand module attributes?
push_context (ITEM);
// expand attributes recursively and strip items if required
AttrVisitor attr_visitor (*this);
auto &items = crate.items;
for (auto it = items.begin (); it != items.end ();)
{
auto &item = *it;
// mark for stripping if required
item->accept_vis (attr_visitor);
if (item->is_marked_for_strip ())
it = items.erase (it);
else
++it;
}
pop_context ();
// TODO: should recursive attribute and macro expansion be done in the same
// transversal? Or in separate ones like currently?
// expand module tree recursively
// post-process
// extract exported macros?
}
bool
MacroExpander::depth_exceeds_recursion_limit () const
{
return expansion_depth >= cfg.recursion_limit;
}
bool
MacroExpander::try_match_rule (AST::MacroRule &match_rule,
AST::DelimTokenTree &invoc_token_tree)
{
MacroInvocLexer lex (invoc_token_tree.to_token_stream ());
Parser parser (std::move (lex));
AST::MacroMatcher &matcher = match_rule.get_matcher ();
expansion_depth++;
if (!match_matcher (parser, matcher))
{
expansion_depth--;
return false;
}
expansion_depth--;
bool used_all_input_tokens = parser.skip_token (END_OF_FILE);
return used_all_input_tokens;
}
bool
MacroExpander::match_fragment (Parser &parser,
AST::MacroMatchFragment &fragment)
{
switch (fragment.get_frag_spec ())
{
case AST::MacroFragSpec::EXPR:
parser.parse_expr ();
break;
case AST::MacroFragSpec::BLOCK:
parser.parse_block_expr ();
break;
case AST::MacroFragSpec::IDENT:
parser.parse_identifier_pattern ();
break;
case AST::MacroFragSpec::LITERAL:
parser.parse_literal_expr ();
break;
case AST::MacroFragSpec::ITEM:
parser.parse_item (false);
break;
case AST::MacroFragSpec::TY:
parser.parse_type ();
break;
case AST::MacroFragSpec::PAT:
parser.parse_pattern ();
break;
case AST::MacroFragSpec::PATH:
parser.parse_path_in_expression ();
break;
case AST::MacroFragSpec::VIS:
parser.parse_visibility ();
break;
case AST::MacroFragSpec::STMT:
parser.parse_stmt ();
break;
case AST::MacroFragSpec::LIFETIME:
parser.parse_lifetime_params ();
break;
// is meta attributes?
case AST::MacroFragSpec::META:
// parser.parse_inner_attribute ?
// parser.parse_outer_attribute ?
// parser.parse_attribute_body ?
// parser.parse_doc_comment ?
gcc_unreachable ();
break;
// what is TT?
case AST::MacroFragSpec::TT:
// parser.parse_token_tree() ?
gcc_unreachable ();
break;
// i guess we just ignore invalid and just error out
case AST::MacroFragSpec::INVALID:
return false;
}
// it matches if the parser did not produce errors trying to parse that type
// of item
return !parser.has_errors ();
}
bool
MacroExpander::match_matcher (Parser &parser,
AST::MacroMatcher &matcher)
{
if (depth_exceeds_recursion_limit ())
{
rust_error_at (matcher.get_match_locus (), "reached recursion limit");
return false;
}
// this is used so we can check that we delimit the stream correctly.
switch (matcher.get_delim_type ())
{
case AST::DelimType::PARENS: {
if (!parser.skip_token (LEFT_PAREN))
return false;
}
break;
case AST::DelimType::SQUARE: {
if (!parser.skip_token (LEFT_SQUARE))
return false;
}
break;
case AST::DelimType::CURLY: {
if (!parser.skip_token (LEFT_CURLY))
return false;
}
break;
}
const MacroInvocLexer &source = parser.get_token_source ();
for (auto &match : matcher.get_matches ())
{
size_t offs_begin = source.get_offs ();
switch (match->get_macro_match_type ())
{
case AST::MacroMatch::MacroMatchType::Fragment: {
AST::MacroMatchFragment *fragment
= static_cast (match.get ());
if (!match_fragment (parser, *fragment))
return false;
// matched fragment get the offset in the token stream
size_t offs_end = source.get_offs ();
sub_stack.insert_fragment (
MatchedFragment (fragment->get_ident (), offs_begin, offs_end));
}
break;
case AST::MacroMatch::MacroMatchType::Tok: {
AST::Token *tok = static_cast (match.get ());
if (!match_token (parser, *tok))
return false;
}
break;
case AST::MacroMatch::MacroMatchType::Repetition: {
AST::MacroMatchRepetition *rep
= static_cast (match.get ());
if (!match_repetition (parser, *rep))
return false;
}
break;
case AST::MacroMatch::MacroMatchType::Matcher: {
AST::MacroMatcher *m
= static_cast (match.get ());
expansion_depth++;
if (!match_matcher (parser, *m))
{
expansion_depth--;
return false;
}
expansion_depth--;
}
break;
}
}
switch (matcher.get_delim_type ())
{
case AST::DelimType::PARENS: {
if (!parser.skip_token (RIGHT_PAREN))
return false;
}
break;
case AST::DelimType::SQUARE: {
if (!parser.skip_token (RIGHT_SQUARE))
return false;
}
break;
case AST::DelimType::CURLY: {
if (!parser.skip_token (RIGHT_CURLY))
return false;
}
break;
}
return true;
}
bool
MacroExpander::match_token (Parser &parser, AST::Token &token)
{
// FIXME this needs to actually match the content and the type
return parser.skip_token (token.get_id ());
}
bool
MacroExpander::match_n_matches (
Parser &parser,
std::vector> &matches, size_t &match_amount,
size_t lo_bound, size_t hi_bound)
{
match_amount = 0;
const MacroInvocLexer &source = parser.get_token_source ();
while (true)
{
// If the current token is a closing macro delimiter, break away.
// TODO: Is this correct?
auto t_id = parser.peek_current_token ()->get_id ();
if (t_id == RIGHT_PAREN || t_id == RIGHT_SQUARE || t_id == RIGHT_CURLY)
break;
bool valid_current_match = false;
for (auto &match : matches)
{
size_t offs_begin = source.get_offs ();
switch (match->get_macro_match_type ())
{
case AST::MacroMatch::MacroMatchType::Fragment: {
AST::MacroMatchFragment *fragment
= static_cast (match.get ());
valid_current_match = match_fragment (parser, *fragment);
// matched fragment get the offset in the token stream
size_t offs_end = source.get_offs ();
sub_stack.insert_fragment (
MatchedFragment (fragment->get_ident (), offs_begin,
offs_end));
}
break;
case AST::MacroMatch::MacroMatchType::Tok: {
AST::Token *tok = static_cast (match.get ());
valid_current_match = match_token (parser, *tok);
}
break;
case AST::MacroMatch::MacroMatchType::Repetition: {
AST::MacroMatchRepetition *rep
= static_cast (match.get ());
valid_current_match = match_repetition (parser, *rep);
}
break;
case AST::MacroMatch::MacroMatchType::Matcher: {
AST::MacroMatcher *m
= static_cast (match.get ());
valid_current_match = match_matcher (parser, *m);
}
break;
}
}
// If we've encountered an error once, stop trying to match more
// repetitions
if (!valid_current_match)
break;
match_amount++;
// Break early if we notice there's too many expressions already
if (hi_bound && match_amount > hi_bound)
break;
}
// Check if the amount of matches we got is valid: Is it more than the lower
// bound and less than the higher bound?
bool did_meet_lo_bound = match_amount >= lo_bound;
bool did_meet_hi_bound = hi_bound ? match_amount <= hi_bound : true;
return did_meet_lo_bound && did_meet_hi_bound;
}
bool
MacroExpander::match_repetition (Parser &parser,
AST::MacroMatchRepetition &rep)
{
size_t match_amount = 0;
bool res = false;
std::string lo_str;
std::string hi_str;
switch (rep.get_op ())
{
case AST::MacroMatchRepetition::MacroRepOp::ANY:
lo_str = "0";
hi_str = "+inf";
res = match_n_matches (parser, rep.get_matches (), match_amount);
break;
case AST::MacroMatchRepetition::MacroRepOp::ONE_OR_MORE:
lo_str = "1";
hi_str = "+inf";
res = match_n_matches (parser, rep.get_matches (), match_amount, 1);
break;
case AST::MacroMatchRepetition::MacroRepOp::ZERO_OR_ONE:
lo_str = "0";
hi_str = "1";
res = match_n_matches (parser, rep.get_matches (), match_amount, 0, 1);
break;
default:
gcc_unreachable ();
}
if (!res)
rust_error_at (rep.get_match_locus (),
"invalid amount of matches for macro invocation. Expected "
"between %s and %s, got %lu",
lo_str.c_str (), hi_str.c_str (), match_amount);
rust_debug_loc (rep.get_match_locus (), "%s matched %lu times",
res ? "successfully" : "unsuccessfully", match_amount);
// We can now set the amount to each fragment we matched in the substack
auto &stack_map = sub_stack.peek ();
for (auto &match : rep.get_matches ())
{
if (match->get_macro_match_type ()
== AST::MacroMatch::MacroMatchType::Fragment)
{
auto fragment = static_cast (match.get ());
auto it = stack_map.find (fragment->get_ident ());
// If we can't find the fragment, but the result was valid, then
// it's a zero-matched fragment and we can insert it
if (it == stack_map.end ())
{
sub_stack.insert_fragment (
MatchedFragment::zero (fragment->get_ident ()));
}
else
{
// We can just set the repetition amount on the first match
// FIXME: Make this more ergonomic and similar to what we fetch
// in `substitute_repetition`
it->second[0].set_match_amount (match_amount);
}
}
}
return res;
}
AST::ASTFragment
MacroExpander::transcribe_rule (
AST::MacroRule &match_rule, AST::DelimTokenTree &invoc_token_tree,
std::map> &matched_fragments,
bool semicolon, ContextType ctx)
{
// we can manipulate the token tree to substitute the dollar identifiers so
// that when we call parse its already substituted for us
AST::MacroTranscriber &transcriber = match_rule.get_transcriber ();
AST::DelimTokenTree &transcribe_tree = transcriber.get_token_tree ();
auto invoc_stream = invoc_token_tree.to_token_stream ();
auto macro_rule_tokens = transcribe_tree.to_token_stream ();
std::vector> substituted_tokens
= substitute_tokens (invoc_stream, macro_rule_tokens, matched_fragments);
// // handy for debugging
// for (auto &tok : substituted_tokens)
// {
// rust_debug ("tok: [%s]", tok->as_string ().c_str ());
// }
// parse it to an ASTFragment
MacroInvocLexer lex (std::move (substituted_tokens));
Parser parser (std::move (lex));
// this is used so we can check that we delimit the stream correctly.
switch (transcribe_tree.get_delim_type ())
{
case AST::DelimType::PARENS:
rust_assert (parser.skip_token (LEFT_PAREN));
break;
case AST::DelimType::CURLY:
rust_assert (parser.skip_token (LEFT_CURLY));
break;
case AST::DelimType::SQUARE:
rust_assert (parser.skip_token (LEFT_SQUARE));
break;
}
// see https://github.com/Rust-GCC/gccrs/issues/22
// TL;DR:
// - Treat all macro invocations with parentheses, (), or square brackets,
// [], as expressions.
// - If the macro invocation has curly brackets, {}, it may be parsed as a
// statement depending on the context.
// - If the macro invocation has a semicolon at the end, it must be parsed
// as a statement (either via ExpressionStatement or
// MacroInvocationWithSemi)
// parse the item
std::vector nodes;
switch (invoc_token_tree.get_delim_type ())
{
case AST::DelimType::PARENS:
case AST::DelimType::SQUARE: {
switch (ctx)
{
case ContextType::ITEM: {
auto item = parser.parse_item (true);
if (item != nullptr && !parser.has_errors ())
{
rust_debug ("HELLO WORLD: [%s]", item->as_string ().c_str ());
nodes.push_back (std::move (item));
}
}
break;
case ContextType::BLOCK: {
auto expr = parser.parse_expr ();
if (expr != nullptr && !parser.has_errors ())
nodes.push_back (std::move (expr));
}
break;
}
}
break;
case AST::DelimType::CURLY: {
switch (ctx)
{
case ContextType::ITEM: {
auto item = parser.parse_item (true);
if (item != nullptr && !parser.has_errors ())
nodes.push_back (std::move (item));
}
break;
case ContextType::BLOCK: {
auto stmt = parser.parse_stmt ();
if (stmt != nullptr && !parser.has_errors ())
nodes.push_back (std::move (stmt));
}
break;
}
}
break;
}
// emit any errors
if (parser.has_errors ())
{
for (auto &err : parser.get_errors ())
{
rust_error_at (err.locus, "%s", err.message.c_str ());
}
return AST::ASTFragment::create_empty ();
}
// are all the tokens used?
bool did_delimit = false;
switch (transcribe_tree.get_delim_type ())
{
case AST::DelimType::PARENS:
did_delimit = parser.skip_token (RIGHT_PAREN);
break;
case AST::DelimType::SQUARE:
did_delimit = parser.skip_token (RIGHT_SQUARE);
break;
case AST::DelimType::CURLY:
did_delimit = parser.skip_token (RIGHT_CURLY);
break;
}
bool reached_end_of_stream = did_delimit && parser.skip_token (END_OF_FILE);
if (!reached_end_of_stream)
{
const_TokenPtr current_token = parser.peek_current_token ();
rust_error_at (current_token->get_locus (),
"tokens here and after are unparsed");
}
return AST::ASTFragment (std::move (nodes));
}
std::vector>
MacroExpander::substitute_metavar (
std::vector> &input,
std::map> &fragments,
std::unique_ptr &metavar)
{
auto metavar_name = metavar->get_str ();
std::vector> expanded;
auto it = fragments.find (metavar_name);
if (it == fragments.end ())
{
// Return a copy of the original token
expanded.push_back (metavar->clone_token ());
}
else
{
// Replace
// We only care about the vector when expanding repetitions. Just access
// the first element of the vector.
// FIXME: Clean this up so it makes more sense
auto &frag = it->second[0];
for (size_t offs = frag.token_offset_begin; offs < frag.token_offset_end;
offs++)
{
auto &tok = input.at (offs);
expanded.push_back (tok->clone_token ());
}
}
return expanded;
}
std::vector>
MacroExpander::substitute_repetition (
std::vector> &input,
std::vector> ¯o,
std::map> &fragments,
size_t pattern_start, size_t pattern_end)
{
rust_assert (pattern_end < macro.size ());
rust_debug ("pattern start: %lu", pattern_start);
rust_debug ("pattern end: %lu", pattern_end);
std::vector> expanded;
// Find the first fragment and get the amount of repetitions that we should
// perform
size_t repeat_amount = 0;
for (size_t i = pattern_start; i < pattern_end; i++)
{
if (macro.at (i)->get_id () == DOLLAR_SIGN)
{
auto &frag_token = macro.at (i + 1);
if (frag_token->get_id () == IDENTIFIER)
{
auto it = fragments.find (frag_token->get_str ());
if (it == fragments.end ())
{
// If the repetition is not anything we know (ie no declared
// metavars, or metavars which aren't present in the
// fragment), we can just error out. No need to paste the
// tokens as if nothing had happened.
rust_error_at (frag_token->get_locus (),
"metavar %s used in repetition does not exist",
frag_token->get_str ().c_str ());
// FIXME:
return expanded;
}
// FIXME: Refactor, ugly
repeat_amount = it->second[0].match_amount;
}
}
}
rust_debug ("repetition amount to use: %lu", repeat_amount);
std::vector> new_macro;
// We want to generate a "new macro" to substitute with. This new macro
// should contain only the tokens inside the pattern
for (size_t tok_idx = pattern_start; tok_idx < pattern_end; tok_idx++)
new_macro.emplace_back (macro.at (tok_idx)->clone_token ());
// Then, we want to create a subset of the matches so that
// `substitute_tokens()` can only see one fragment per metavar. Let's say we
// have the following user input: (1 145 'h')
// on the following match arm: ($($lit:literal)*)
// which causes the following matches: { "lit": [1, 145, 'h'] }
//
// The pattern (new_macro) is `$lit:literal`
// The first time we expand it, we want $lit to have the following token: 1
// The second time, 145
// The third and final time, 'h'
//
// In order to do so we must create "sub maps", which only contain parts of
// the original matches
// sub-maps: [ { "lit": 1 }, { "lit": 145 }, { "lit": 'h' } ]
//
// and give them to `substitute_tokens` one by one.
for (size_t i = 0; i < repeat_amount; i++)
{
std::map> sub_map;
for (auto &kv_match : fragments)
{
std::vector sub_vec;
sub_vec.emplace_back (kv_match.second[i]);
sub_map.insert ({kv_match.first, sub_vec});
}
auto new_tokens = substitute_tokens (input, new_macro, sub_map);
for (auto &new_token : new_tokens)
expanded.emplace_back (new_token->clone_token ());
}
// FIXME: We also need to make sure that all subsequent fragments
// contain the same amount of repetitions as the first one
return expanded;
}
std::pair>, size_t>
MacroExpander::substitute_token (
std::vector> &input,
std::vector> ¯o,
std::map> &fragments,
size_t token_idx)
{
auto &token = macro.at (token_idx);
switch (token->get_id ())
{
case IDENTIFIER:
rust_debug ("expanding metavar: %s", token->get_str ().c_str ());
return {substitute_metavar (input, fragments, token), 1};
case LEFT_PAREN: {
// We need to parse up until the closing delimiter and expand this
// fragment->n times.
rust_debug ("expanding repetition");
std::vector> repetition_pattern;
size_t pattern_start = token_idx + 1;
size_t pattern_end = pattern_start;
for (; pattern_end < macro.size ()
&& macro.at (pattern_end)->get_id () != RIGHT_PAREN;
pattern_end++)
;
// FIXME: This skips whitespaces... Is that okay??
// FIXME: Is there any existing parsing function that allows us to parse
// a macro pattern?
// FIXME: Add error handling in the case we haven't found a matching
// closing delimiter
// FIXME: We need to parse the repetition token now
return {
substitute_repetition (input, macro, fragments, pattern_start,
pattern_end),
// + 2 for the opening and closing parentheses which are mandatory
// + 1 for the repetitor (+, *, ?)
pattern_end - pattern_start + 3};
}
// TODO: We need to check if the $ was alone. In that case, do
// not error out: Simply act as if there was an empty identifier
// with no associated fragment and paste the dollar sign in the
// transcription. Unsure how to do that since we always have at
// least the closing curly brace after an empty $...
default:
rust_error_at (token->get_locus (),
"unexpected token in macro transcribe: expected "
"%<(%> or identifier after %<$%>, got %<%s%>",
get_token_description (token->get_id ()));
}
// FIXME: gcc_unreachable() error case?
return {std::vector> (), 0};
}
std::vector>
MacroExpander::substitute_tokens (
std::vector> &input,
std::vector> ¯o,
std::map> &fragments)
{
std::vector> replaced_tokens;
for (size_t i = 0; i < macro.size (); i++)
{
auto &tok = macro.at (i);
if (tok->get_id () == DOLLAR_SIGN)
{
// Aaaaah, if only we had C++17 :)
// auto [expanded, tok_to_skip] = ...
auto p = substitute_token (input, macro, fragments, i + 1);
auto expanded = std::move (p.first);
auto tok_to_skip = p.second;
i += tok_to_skip;
for (auto &token : expanded)
replaced_tokens.emplace_back (token->clone_token ());
}
else
{
replaced_tokens.emplace_back (tok->clone_token ());
}
}
return replaced_tokens;
}
} // namespace Rust