// Copyright (C) 2020-2023 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
// <http://www.gnu.org/licenses/>.

#ifndef RUST_AST_PATTERN_H
#define RUST_AST_PATTERN_H

#include "rust-ast.h"

namespace Rust {
namespace AST {
// Literal pattern AST node (comparing to a literal)
class LiteralPattern : public Pattern
{
  Literal lit;
  Location locus;
  NodeId node_id;

public:
  std::string as_string () const override;

  // Constructor for a literal pattern
  LiteralPattern (Literal lit, Location locus)
    : lit (std::move (lit)), locus (locus),
      node_id (Analysis::Mappings::get ()->get_next_node_id ())
  {}

  LiteralPattern (std::string val, Literal::LitType type, Location locus)
    : lit (Literal (std::move (val), type, PrimitiveCoreType::CORETYPE_STR)),
      locus (locus), node_id (Analysis::Mappings::get ()->get_next_node_id ())
  {}

  Location get_locus () const override final { return locus; }

  void accept_vis (ASTVisitor &vis) override;

  NodeId get_node_id () const { return node_id; }

  NodeId get_pattern_node_id () const override final { return node_id; }

  Literal &get_literal () { return lit; }

  const Literal &get_literal () const { return lit; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  virtual LiteralPattern *clone_pattern_impl () const override
  {
    return new LiteralPattern (*this);
  }
};

// Identifier pattern AST node (bind value matched to a variable)
class IdentifierPattern : public Pattern
{
  Identifier variable_ident;
  bool is_ref;
  bool is_mut;

  // bool has_pattern;
  std::unique_ptr<Pattern> to_bind;
  Location locus;
  NodeId node_id;

public:
  std::string as_string () const override;

  // Returns whether the IdentifierPattern has a pattern to bind.
  bool has_pattern_to_bind () const { return to_bind != nullptr; }

  // Constructor
  IdentifierPattern (Identifier ident, Location locus, bool is_ref = false,
		     bool is_mut = false,
		     std::unique_ptr<Pattern> to_bind = nullptr)
    : Pattern (), variable_ident (std::move (ident)), is_ref (is_ref),
      is_mut (is_mut), to_bind (std::move (to_bind)), locus (locus),
      node_id (Analysis::Mappings::get ()->get_next_node_id ())
  {}

  IdentifierPattern (NodeId node_id, Identifier ident, Location locus,
		     bool is_ref = false, bool is_mut = false,
		     std::unique_ptr<Pattern> to_bind = nullptr)
    : Pattern (), variable_ident (std::move (ident)), is_ref (is_ref),
      is_mut (is_mut), to_bind (std::move (to_bind)), locus (locus),
      node_id (node_id)
  {}

  // Copy constructor with clone
  IdentifierPattern (IdentifierPattern const &other)
    : variable_ident (other.variable_ident), is_ref (other.is_ref),
      is_mut (other.is_mut), locus (other.locus), node_id (other.node_id)
  {
    // fix to get prevent null pointer dereference
    if (other.to_bind != nullptr)
      to_bind = other.to_bind->clone_pattern ();
  }

  // Overload assignment operator to use clone
  IdentifierPattern &operator= (IdentifierPattern const &other)
  {
    variable_ident = other.variable_ident;
    is_ref = other.is_ref;
    is_mut = other.is_mut;
    locus = other.locus;
    node_id = other.node_id;

    // fix to prevent null pointer dereference
    if (other.to_bind != nullptr)
      to_bind = other.to_bind->clone_pattern ();
    else
      to_bind = nullptr;

    return *this;
  }

  // default move semantics
  IdentifierPattern (IdentifierPattern &&other) = default;
  IdentifierPattern &operator= (IdentifierPattern &&other) = default;

  Location get_locus () const override final { return locus; }

  void accept_vis (ASTVisitor &vis) override;

  // TODO: is this better? Or is a "vis_pattern" better?
  std::unique_ptr<Pattern> &get_pattern_to_bind ()
  {
    rust_assert (has_pattern_to_bind ());
    return to_bind;
  }

  Identifier get_ident () const { return variable_ident; }

  bool get_is_mut () const { return is_mut; }
  bool get_is_ref () const { return is_ref; }

  NodeId get_node_id () const { return node_id; }

  NodeId get_pattern_node_id () const override final { return node_id; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  IdentifierPattern *clone_pattern_impl () const override
  {
    return new IdentifierPattern (*this);
  }
};

// AST node for using the '_' wildcard "match any value" pattern
class WildcardPattern : public Pattern
{
  Location locus;
  NodeId node_id;

public:
  std::string as_string () const override { return std::string (1, '_'); }

  WildcardPattern (Location locus)
    : locus (locus), node_id (Analysis::Mappings::get ()->get_next_node_id ())
  {}

  Location get_locus () const override final { return locus; }

  void accept_vis (ASTVisitor &vis) override;

  NodeId get_node_id () const { return node_id; }

  NodeId get_pattern_node_id () const override final { return node_id; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  WildcardPattern *clone_pattern_impl () const override
  {
    return new WildcardPattern (*this);
  }
};

// Base range pattern bound (lower or upper limit) - abstract
class RangePatternBound
{
public:
  enum RangePatternBoundType
  {
    LITERAL,
    PATH,
    QUALPATH
  };

  virtual ~RangePatternBound () {}

  // Unique pointer custom clone function
  std::unique_ptr<RangePatternBound> clone_range_pattern_bound () const
  {
    return std::unique_ptr<RangePatternBound> (
      clone_range_pattern_bound_impl ());
  }

  virtual std::string as_string () const = 0;

  virtual void accept_vis (ASTVisitor &vis) = 0;

  virtual RangePatternBoundType get_bound_type () const = 0;

protected:
  // pure virtual as RangePatternBound is abstract
  virtual RangePatternBound *clone_range_pattern_bound_impl () const = 0;
};

// Literal-based pattern bound
class RangePatternBoundLiteral : public RangePatternBound
{
  Literal literal;
  /* Can only be a char, byte, int, or float literal - same impl here as
   * previously */

  // Minus prefixed to literal (if integer or floating-point)
  bool has_minus;

  Location locus;

public:
  // Constructor
  RangePatternBoundLiteral (Literal literal, Location locus,
			    bool has_minus = false)
    : literal (literal), has_minus (has_minus), locus (locus)
  {}

  std::string as_string () const override;

  Literal get_literal () const { return literal; }

  bool get_has_minus () const { return has_minus; }

  Location get_locus () const { return locus; }

  void accept_vis (ASTVisitor &vis) override;

  RangePatternBoundType get_bound_type () const override
  {
    return RangePatternBoundType::LITERAL;
  }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  RangePatternBoundLiteral *clone_range_pattern_bound_impl () const override
  {
    return new RangePatternBoundLiteral (*this);
  }
};

// Path-based pattern bound
class RangePatternBoundPath : public RangePatternBound
{
  PathInExpression path;

  /* TODO: should this be refactored so that PathInExpression is a subclass of
   * RangePatternBound? */

public:
  RangePatternBoundPath (PathInExpression path) : path (std::move (path)) {}

  std::string as_string () const override { return path.as_string (); }

  Location get_locus () const { return path.get_locus (); }

  void accept_vis (ASTVisitor &vis) override;

  // TODO: this mutable getter seems kinda dodgy
  PathInExpression &get_path () { return path; }
  const PathInExpression &get_path () const { return path; }

  RangePatternBoundType get_bound_type () const override
  {
    return RangePatternBoundType::PATH;
  }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  RangePatternBoundPath *clone_range_pattern_bound_impl () const override
  {
    return new RangePatternBoundPath (*this);
  }
};

// Qualified path-based pattern bound
class RangePatternBoundQualPath : public RangePatternBound
{
  QualifiedPathInExpression path;

  /* TODO: should this be refactored so that QualifiedPathInExpression is a
   * subclass of RangePatternBound? */

public:
  RangePatternBoundQualPath (QualifiedPathInExpression path)
    : path (std::move (path))
  {}

  std::string as_string () const override { return path.as_string (); }

  Location get_locus () const { return path.get_locus (); }

  void accept_vis (ASTVisitor &vis) override;

  // TODO: this mutable getter seems kinda dodgy
  QualifiedPathInExpression &get_qualified_path () { return path; }
  const QualifiedPathInExpression &get_qualified_path () const { return path; }

  RangePatternBoundType get_bound_type () const override
  {
    return RangePatternBoundType::QUALPATH;
  }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  RangePatternBoundQualPath *clone_range_pattern_bound_impl () const override
  {
    return new RangePatternBoundQualPath (*this);
  }
};

// AST node for matching within a certain range (range pattern)
class RangePattern : public Pattern
{
  std::unique_ptr<RangePatternBound> lower;
  std::unique_ptr<RangePatternBound> upper;

  bool has_ellipsis_syntax;

  /* location only stored to avoid a dereference - lower pattern should give
   * correct location so maybe change in future */
  Location locus;
  NodeId node_id;

public:
  std::string as_string () const override;

  // Constructor
  RangePattern (std::unique_ptr<RangePatternBound> lower,
		std::unique_ptr<RangePatternBound> upper, Location locus,
		bool has_ellipsis_syntax = false)
    : lower (std::move (lower)), upper (std::move (upper)),
      has_ellipsis_syntax (has_ellipsis_syntax), locus (locus),
      node_id (Analysis::Mappings::get ()->get_next_node_id ())
  {}

  // Copy constructor with clone
  RangePattern (RangePattern const &other)
    : lower (other.lower->clone_range_pattern_bound ()),
      upper (other.upper->clone_range_pattern_bound ()),
      has_ellipsis_syntax (other.has_ellipsis_syntax), locus (other.locus),
      node_id (other.node_id)
  {}

  // Overloaded assignment operator to clone
  RangePattern &operator= (RangePattern const &other)
  {
    lower = other.lower->clone_range_pattern_bound ();
    upper = other.upper->clone_range_pattern_bound ();
    has_ellipsis_syntax = other.has_ellipsis_syntax;
    locus = other.locus;
    node_id = other.node_id;

    return *this;
  }

  // default move semantics
  RangePattern (RangePattern &&other) = default;
  RangePattern &operator= (RangePattern &&other) = default;

  Location get_locus () const override final { return locus; }

  void accept_vis (ASTVisitor &vis) override;

  // TODO: is this better? or is a "vis_bound" better?
  std::unique_ptr<RangePatternBound> &get_lower_bound ()
  {
    rust_assert (lower != nullptr);
    return lower;
  }

  std::unique_ptr<RangePatternBound> &get_upper_bound ()
  {
    rust_assert (upper != nullptr);
    return upper;
  }

  NodeId get_node_id () const { return node_id; }

  NodeId get_pattern_node_id () const override final { return node_id; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  RangePattern *clone_pattern_impl () const override
  {
    return new RangePattern (*this);
  }
};

// AST node for pattern based on dereferencing the pointers given
class ReferencePattern : public Pattern
{
  bool has_two_amps;
  bool is_mut;
  std::unique_ptr<Pattern> pattern;
  Location locus;
  NodeId node_id;

public:
  std::string as_string () const override;

  ReferencePattern (std::unique_ptr<Pattern> pattern, bool is_mut_reference,
		    bool ref_has_two_amps, Location locus)
    : has_two_amps (ref_has_two_amps), is_mut (is_mut_reference),
      pattern (std::move (pattern)), locus (locus),
      node_id (Analysis::Mappings::get ()->get_next_node_id ())
  {}

  // Copy constructor requires clone
  ReferencePattern (ReferencePattern const &other)
    : has_two_amps (other.has_two_amps), is_mut (other.is_mut),
      pattern (other.pattern->clone_pattern ()), locus (other.locus),
      node_id (other.node_id)
  {}

  // Overload assignment operator to clone
  ReferencePattern &operator= (ReferencePattern const &other)
  {
    pattern = other.pattern->clone_pattern ();
    is_mut = other.is_mut;
    has_two_amps = other.has_two_amps;
    locus = other.locus;
    node_id = other.node_id;

    return *this;
  }

  // default move semantics
  ReferencePattern (ReferencePattern &&other) = default;
  ReferencePattern &operator= (ReferencePattern &&other) = default;

  Location get_locus () const override final { return locus; }

  void accept_vis (ASTVisitor &vis) override;

  // TODO: is this better? Or is a "vis_pattern" better?
  std::unique_ptr<Pattern> &get_referenced_pattern ()
  {
    rust_assert (pattern != nullptr);
    return pattern;
  }

  bool is_double_reference () const { return has_two_amps; }

  bool get_is_mut () const { return is_mut; }

  NodeId get_node_id () const { return node_id; }

  NodeId get_pattern_node_id () const override final { return node_id; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  ReferencePattern *clone_pattern_impl () const override
  {
    return new ReferencePattern (*this);
  }
};

#if 0
// aka StructPatternEtCetera; potential element in struct pattern
struct StructPatternEtc
{
private:
  std::vector<Attribute> outer_attrs;

  // should this store location data?

public:
  StructPatternEtc (std::vector<Attribute> outer_attribs)
    : outer_attrs (std::move (outer_attribs))
  {}

  // Creates an empty StructPatternEtc
  static StructPatternEtc create_empty ()
  {
    return StructPatternEtc (std::vector<Attribute> ());
  }
};
#endif

// Base class for a single field in a struct pattern - abstract
class StructPatternField
{
  std::vector<Attribute> outer_attrs;
  Location locus;

protected:
  NodeId node_id;

public:
  enum ItemType
  {
    TUPLE_PAT,
    IDENT_PAT,
    IDENT
  };

  virtual ~StructPatternField () {}

  // Unique pointer custom clone function
  std::unique_ptr<StructPatternField> clone_struct_pattern_field () const
  {
    return std::unique_ptr<StructPatternField> (
      clone_struct_pattern_field_impl ());
  }

  virtual std::string as_string () const;

  Location get_locus () const { return locus; }

  virtual void accept_vis (ASTVisitor &vis) = 0;

  virtual void mark_for_strip () = 0;
  virtual bool is_marked_for_strip () const = 0;
  virtual ItemType get_item_type () const = 0;

  NodeId get_node_id () const { return node_id; }

  // TODO: seems kinda dodgy. Think of better way.
  std::vector<Attribute> &get_outer_attrs () { return outer_attrs; }
  const std::vector<Attribute> &get_outer_attrs () const { return outer_attrs; }

protected:
  StructPatternField (std::vector<Attribute> outer_attribs, Location locus,
		      NodeId node_id)
    : outer_attrs (std::move (outer_attribs)), locus (locus), node_id (node_id)
  {}

  // Clone function implementation as pure virtual method
  virtual StructPatternField *clone_struct_pattern_field_impl () const = 0;
};

// Tuple pattern single field in a struct pattern
class StructPatternFieldTuplePat : public StructPatternField
{
  TupleIndex index;
  std::unique_ptr<Pattern> tuple_pattern;

public:
  StructPatternFieldTuplePat (TupleIndex index,
			      std::unique_ptr<Pattern> tuple_pattern,
			      std::vector<Attribute> outer_attribs,
			      Location locus)
    : StructPatternField (std::move (outer_attribs), locus,
			  Analysis::Mappings::get ()->get_next_node_id ()),
      index (index), tuple_pattern (std::move (tuple_pattern))
  {}

  // Copy constructor requires clone
  StructPatternFieldTuplePat (StructPatternFieldTuplePat const &other)
    : StructPatternField (other), index (other.index)
  {
    // guard to prevent null dereference (only required if error state)
    node_id = other.get_node_id ();
    if (other.tuple_pattern != nullptr)
      tuple_pattern = other.tuple_pattern->clone_pattern ();
  }

  // Overload assignment operator to perform clone
  StructPatternFieldTuplePat &
  operator= (StructPatternFieldTuplePat const &other)
  {
    StructPatternField::operator= (other);
    index = other.index;
    // outer_attrs = other.outer_attrs;
    node_id = other.get_node_id ();

    // guard to prevent null dereference (only required if error state)
    if (other.tuple_pattern != nullptr)
      tuple_pattern = other.tuple_pattern->clone_pattern ();
    else
      tuple_pattern = nullptr;

    return *this;
  }

  // default move semantics
  StructPatternFieldTuplePat (StructPatternFieldTuplePat &&other) = default;
  StructPatternFieldTuplePat &operator= (StructPatternFieldTuplePat &&other)
    = default;

  std::string as_string () const override;

  void accept_vis (ASTVisitor &vis) override;

  // based on idea of tuple pattern no longer existing
  void mark_for_strip () override { tuple_pattern = nullptr; }
  bool is_marked_for_strip () const override
  {
    return tuple_pattern == nullptr;
  }

  // TODO: is this better? Or is a "vis_pattern" better?
  std::unique_ptr<Pattern> &get_index_pattern ()
  {
    rust_assert (tuple_pattern != nullptr);
    return tuple_pattern;
  }

  ItemType get_item_type () const override final { return ItemType::TUPLE_PAT; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  StructPatternFieldTuplePat *clone_struct_pattern_field_impl () const override
  {
    return new StructPatternFieldTuplePat (*this);
  }
};

// Identifier pattern single field in a struct pattern
class StructPatternFieldIdentPat : public StructPatternField
{
  Identifier ident;
  std::unique_ptr<Pattern> ident_pattern;

public:
  StructPatternFieldIdentPat (Identifier ident,
			      std::unique_ptr<Pattern> ident_pattern,
			      std::vector<Attribute> outer_attrs,
			      Location locus)
    : StructPatternField (std::move (outer_attrs), locus,
			  Analysis::Mappings::get ()->get_next_node_id ()),
      ident (std::move (ident)), ident_pattern (std::move (ident_pattern))
  {}

  // Copy constructor requires clone
  StructPatternFieldIdentPat (StructPatternFieldIdentPat const &other)
    : StructPatternField (other), ident (other.ident)
  {
    // guard to prevent null dereference (only required if error state)
    node_id = other.get_node_id ();
    if (other.ident_pattern != nullptr)
      ident_pattern = other.ident_pattern->clone_pattern ();
  }

  // Overload assignment operator to clone
  StructPatternFieldIdentPat &
  operator= (StructPatternFieldIdentPat const &other)
  {
    StructPatternField::operator= (other);
    ident = other.ident;
    // outer_attrs = other.outer_attrs;
    node_id = other.get_node_id ();

    // guard to prevent null dereference (only required if error state)
    if (other.ident_pattern != nullptr)
      ident_pattern = other.ident_pattern->clone_pattern ();
    else
      ident_pattern = nullptr;

    return *this;
  }

  // default move semantics
  StructPatternFieldIdentPat (StructPatternFieldIdentPat &&other) = default;
  StructPatternFieldIdentPat &operator= (StructPatternFieldIdentPat &&other)
    = default;

  std::string as_string () const override;

  void accept_vis (ASTVisitor &vis) override;

  // based on idea of identifier pattern no longer existing
  void mark_for_strip () override { ident_pattern = nullptr; }
  bool is_marked_for_strip () const override
  {
    return ident_pattern == nullptr;
  }

  const Identifier &get_identifier () const { return ident; }

  // TODO: is this better? Or is a "vis_pattern" better?
  std::unique_ptr<Pattern> &get_ident_pattern ()
  {
    rust_assert (ident_pattern != nullptr);
    return ident_pattern;
  }

  ItemType get_item_type () const override final { return ItemType::IDENT_PAT; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  StructPatternFieldIdentPat *clone_struct_pattern_field_impl () const override
  {
    return new StructPatternFieldIdentPat (*this);
  }
};

// Identifier only (with no pattern) single field in a struct pattern
class StructPatternFieldIdent : public StructPatternField
{
  bool has_ref;
  bool has_mut;
  Identifier ident;

public:
  StructPatternFieldIdent (Identifier ident, bool is_ref, bool is_mut,
			   std::vector<Attribute> outer_attrs, Location locus)
    : StructPatternField (std::move (outer_attrs), locus,
			  Analysis::Mappings::get ()->get_next_node_id ()),
      has_ref (is_ref), has_mut (is_mut), ident (std::move (ident))
  {}

  std::string as_string () const override;

  void accept_vis (ASTVisitor &vis) override;

  // based on idea of identifier no longer existing
  void mark_for_strip () override { ident = {}; }
  bool is_marked_for_strip () const override { return ident.empty (); }

  const Identifier &get_identifier () const { return ident; }

  ItemType get_item_type () const override final { return ItemType::IDENT; }

  bool is_ref () const { return has_ref; }

  bool is_mut () const { return has_mut; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  StructPatternFieldIdent *clone_struct_pattern_field_impl () const override
  {
    return new StructPatternFieldIdent (*this);
  }
};

// Elements of a struct pattern
struct StructPatternElements
{
private:
  // bool has_struct_pattern_fields;
  std::vector<std::unique_ptr<StructPatternField>> fields;

  bool has_struct_pattern_etc;
  std::vector<Attribute> struct_pattern_etc_attrs;
  // StructPatternEtc etc;

  // must have at least one of the two and maybe both

  // should this store location data?

public:
  // Returns whether there are any struct pattern fields
  bool has_struct_pattern_fields () const { return !fields.empty (); }

  /* Returns whether the struct pattern elements is entirely empty (no fields,
   * no etc). */
  bool is_empty () const
  {
    return !has_struct_pattern_fields () && !has_struct_pattern_etc;
  }

  bool has_etc () const { return has_struct_pattern_etc; }

  // Constructor for StructPatternElements with both (potentially)
  StructPatternElements (
    std::vector<std::unique_ptr<StructPatternField>> fields,
    std::vector<Attribute> etc_attrs)
    : fields (std::move (fields)), has_struct_pattern_etc (true),
      struct_pattern_etc_attrs (std::move (etc_attrs))
  {}

  // Constructor for StructPatternElements with no StructPatternEtc
  StructPatternElements (
    std::vector<std::unique_ptr<StructPatternField>> fields)
    : fields (std::move (fields)), has_struct_pattern_etc (false),
      struct_pattern_etc_attrs ()
  {}

  // Copy constructor with vector clone
  StructPatternElements (StructPatternElements const &other)
    : has_struct_pattern_etc (other.has_struct_pattern_etc),
      struct_pattern_etc_attrs (other.struct_pattern_etc_attrs)
  {
    fields.reserve (other.fields.size ());
    for (const auto &e : other.fields)
      fields.push_back (e->clone_struct_pattern_field ());
  }

  // Overloaded assignment operator with vector clone
  StructPatternElements &operator= (StructPatternElements const &other)
  {
    struct_pattern_etc_attrs = other.struct_pattern_etc_attrs;
    has_struct_pattern_etc = other.has_struct_pattern_etc;

    fields.reserve (other.fields.size ());
    for (const auto &e : other.fields)
      fields.push_back (e->clone_struct_pattern_field ());

    return *this;
  }

  // move constructors
  StructPatternElements (StructPatternElements &&other) = default;
  StructPatternElements &operator= (StructPatternElements &&other) = default;

  // Creates an empty StructPatternElements
  static StructPatternElements create_empty ()
  {
    return StructPatternElements (
      std::vector<std::unique_ptr<StructPatternField>> ());
  }

  std::string as_string () const;

  // TODO: seems kinda dodgy. Think of better way.
  std::vector<std::unique_ptr<StructPatternField>> &get_struct_pattern_fields ()
  {
    return fields;
  }
  const std::vector<std::unique_ptr<StructPatternField>> &
  get_struct_pattern_fields () const
  {
    return fields;
  }

  std::vector<Attribute> &get_etc_outer_attrs ()
  {
    return struct_pattern_etc_attrs;
  }
  const std::vector<Attribute> &get_etc_outer_attrs () const
  {
    return struct_pattern_etc_attrs;
  }

  void strip_etc ()
  {
    has_struct_pattern_etc = false;
    struct_pattern_etc_attrs.clear ();
    struct_pattern_etc_attrs.shrink_to_fit ();
  }
};

// Struct pattern AST node representation
class StructPattern : public Pattern
{
  PathInExpression path;

  // bool has_struct_pattern_elements;
  StructPatternElements elems;

  NodeId node_id;
  Location locus;

public:
  std::string as_string () const override;

  // Constructs a struct pattern from specified StructPatternElements
  StructPattern (PathInExpression struct_path, Location locus,
		 StructPatternElements elems
		 = StructPatternElements::create_empty ())
    : path (std::move (struct_path)), elems (std::move (elems)),
      node_id (Analysis::Mappings::get ()->get_next_node_id ()), locus (locus)
  {}

  /* TODO: constructor to construct via elements included in
   * StructPatternElements */

  /* Returns whether struct pattern has any struct pattern elements (if not, it
   * is empty). */
  bool has_struct_pattern_elems () const { return !elems.is_empty (); }

  Location get_locus () const { return path.get_locus (); }

  void accept_vis (ASTVisitor &vis) override;

  // TODO: seems kinda dodgy. Think of better way.
  StructPatternElements &get_struct_pattern_elems () { return elems; }
  const StructPatternElements &get_struct_pattern_elems () const
  {
    return elems;
  }

  PathInExpression &get_path () { return path; }
  const PathInExpression &get_path () const { return path; }

  NodeId get_node_id () const { return node_id; }

  NodeId get_pattern_node_id () const override final { return node_id; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  StructPattern *clone_pattern_impl () const override
  {
    return new StructPattern (*this);
  }
};

// Base abstract class for patterns used in TupleStructPattern
class TupleStructItems
{
public:
  enum ItemType
  {
    RANGE,
    NO_RANGE
  };

  virtual ~TupleStructItems () {}

  // TODO: should this store location data?

  // Unique pointer custom clone function
  std::unique_ptr<TupleStructItems> clone_tuple_struct_items () const
  {
    return std::unique_ptr<TupleStructItems> (clone_tuple_struct_items_impl ());
  }

  virtual std::string as_string () const = 0;

  virtual void accept_vis (ASTVisitor &vis) = 0;

  virtual ItemType get_item_type () const = 0;

protected:
  // pure virtual clone implementation
  virtual TupleStructItems *clone_tuple_struct_items_impl () const = 0;
};

// Class for non-ranged tuple struct pattern patterns
class TupleStructItemsNoRange : public TupleStructItems
{
  std::vector<std::unique_ptr<Pattern>> patterns;

public:
  TupleStructItemsNoRange (std::vector<std::unique_ptr<Pattern>> patterns)
    : patterns (std::move (patterns))
  {}

  // Copy constructor with vector clone
  TupleStructItemsNoRange (TupleStructItemsNoRange const &other)
  {
    patterns.reserve (other.patterns.size ());
    for (const auto &e : other.patterns)
      patterns.push_back (e->clone_pattern ());
  }

  // Overloaded assignment operator with vector clone
  TupleStructItemsNoRange &operator= (TupleStructItemsNoRange const &other)
  {
    patterns.reserve (other.patterns.size ());
    for (const auto &e : other.patterns)
      patterns.push_back (e->clone_pattern ());

    return *this;
  }

  // move constructors
  TupleStructItemsNoRange (TupleStructItemsNoRange &&other) = default;
  TupleStructItemsNoRange &operator= (TupleStructItemsNoRange &&other)
    = default;

  std::string as_string () const override;

  void accept_vis (ASTVisitor &vis) override;

  // TODO: seems kinda dodgy. Think of better way.
  std::vector<std::unique_ptr<Pattern>> &get_patterns () { return patterns; }
  const std::vector<std::unique_ptr<Pattern>> &get_patterns () const
  {
    return patterns;
  }

  ItemType get_item_type () const override final { return ItemType::NO_RANGE; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  TupleStructItemsNoRange *clone_tuple_struct_items_impl () const override
  {
    return new TupleStructItemsNoRange (*this);
  }
};

// Class for ranged tuple struct pattern patterns
class TupleStructItemsRange : public TupleStructItems
{
  std::vector<std::unique_ptr<Pattern>> lower_patterns;
  std::vector<std::unique_ptr<Pattern>> upper_patterns;

public:
  TupleStructItemsRange (std::vector<std::unique_ptr<Pattern>> lower_patterns,
			 std::vector<std::unique_ptr<Pattern>> upper_patterns)
    : lower_patterns (std::move (lower_patterns)),
      upper_patterns (std::move (upper_patterns))
  {}

  // Copy constructor with vector clone
  TupleStructItemsRange (TupleStructItemsRange const &other)
  {
    lower_patterns.reserve (other.lower_patterns.size ());
    for (const auto &e : other.lower_patterns)
      lower_patterns.push_back (e->clone_pattern ());

    upper_patterns.reserve (other.upper_patterns.size ());
    for (const auto &e : other.upper_patterns)
      upper_patterns.push_back (e->clone_pattern ());
  }

  // Overloaded assignment operator to clone
  TupleStructItemsRange &operator= (TupleStructItemsRange const &other)
  {
    lower_patterns.reserve (other.lower_patterns.size ());
    for (const auto &e : other.lower_patterns)
      lower_patterns.push_back (e->clone_pattern ());

    upper_patterns.reserve (other.upper_patterns.size ());
    for (const auto &e : other.upper_patterns)
      upper_patterns.push_back (e->clone_pattern ());

    return *this;
  }

  // move constructors
  TupleStructItemsRange (TupleStructItemsRange &&other) = default;
  TupleStructItemsRange &operator= (TupleStructItemsRange &&other) = default;

  std::string as_string () const override;

  void accept_vis (ASTVisitor &vis) override;

  // TODO: seems kinda dodgy. Think of better way.
  std::vector<std::unique_ptr<Pattern>> &get_lower_patterns ()
  {
    return lower_patterns;
  }
  const std::vector<std::unique_ptr<Pattern>> &get_lower_patterns () const
  {
    return lower_patterns;
  }

  // TODO: seems kinda dodgy. Think of better way.
  std::vector<std::unique_ptr<Pattern>> &get_upper_patterns ()
  {
    return upper_patterns;
  }
  const std::vector<std::unique_ptr<Pattern>> &get_upper_patterns () const
  {
    return upper_patterns;
  }

  ItemType get_item_type () const override final { return ItemType::RANGE; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  TupleStructItemsRange *clone_tuple_struct_items_impl () const override
  {
    return new TupleStructItemsRange (*this);
  }
};

// AST node representing a tuple struct pattern
class TupleStructPattern : public Pattern
{
  PathInExpression path;
  std::unique_ptr<TupleStructItems> items;
  NodeId node_id;

  /* TOOD: should this store location data? current accessor uses path location
   * data */

public:
  std::string as_string () const override;

  // Returns whether the pattern has tuple struct items.
  bool has_items () const { return items != nullptr; }

  TupleStructPattern (PathInExpression tuple_struct_path,
		      std::unique_ptr<TupleStructItems> items)
    : path (std::move (tuple_struct_path)), items (std::move (items)),
      node_id (Analysis::Mappings::get ()->get_next_node_id ())
  {}

  // Copy constructor required to clone
  TupleStructPattern (TupleStructPattern const &other) : path (other.path)
  {
    // guard to protect from null dereference
    node_id = other.node_id;
    if (other.items != nullptr)
      items = other.items->clone_tuple_struct_items ();
  }

  // Operator overload assignment operator to clone
  TupleStructPattern &operator= (TupleStructPattern const &other)
  {
    path = other.path;
    node_id = other.node_id;

    // guard to protect from null dereference
    if (other.items != nullptr)
      items = other.items->clone_tuple_struct_items ();
    else
      items = nullptr;

    return *this;
  }

  // move constructors
  TupleStructPattern (TupleStructPattern &&other) = default;
  TupleStructPattern &operator= (TupleStructPattern &&other) = default;

  Location get_locus () const override { return path.get_locus (); }

  void accept_vis (ASTVisitor &vis) override;

  // TODO: seems kinda dodgy. Think of better way.
  std::unique_ptr<TupleStructItems> &get_items ()
  {
    rust_assert (has_items ());
    return items;
  }

  PathInExpression &get_path () { return path; }
  const PathInExpression &get_path () const { return path; }

  NodeId get_node_id () const { return node_id; }

  NodeId get_pattern_node_id () const override final { return node_id; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  TupleStructPattern *clone_pattern_impl () const override
  {
    return new TupleStructPattern (*this);
  }
};

// Base abstract class representing TuplePattern patterns
class TuplePatternItems
{
public:
  enum TuplePatternItemType
  {
    MULTIPLE,
    RANGED,
  };

  virtual ~TuplePatternItems () {}

  // TODO: should this store location data?

  // Unique pointer custom clone function
  std::unique_ptr<TuplePatternItems> clone_tuple_pattern_items () const
  {
    return std::unique_ptr<TuplePatternItems> (
      clone_tuple_pattern_items_impl ());
  }

  virtual std::string as_string () const = 0;

  virtual void accept_vis (ASTVisitor &vis) = 0;

  virtual TuplePatternItemType get_pattern_type () const = 0;

protected:
  // pure virtual clone implementation
  virtual TuplePatternItems *clone_tuple_pattern_items_impl () const = 0;
};

// Class representing TuplePattern patterns where there is only a single pattern
/*class TuplePatternItemsSingle : public TuplePatternItems {
    // Pattern pattern;
    std::unique_ptr<Pattern> pattern;

  public:
    TuplePatternItemsSingle(Pattern* pattern) : pattern(pattern) {}

    // Copy constructor uses clone
    TuplePatternItemsSingle(TuplePatternItemsSingle const& other) :
      pattern(other.pattern->clone_pattern()) {}

    // Destructor - define here if required

    // Overload assignment operator to clone
    TuplePatternItemsSingle& operator=(TuplePatternItemsSingle const& other) {
	pattern = other.pattern->clone_pattern();

	return *this;
    }

    // move constructors
    TuplePatternItemsSingle(TuplePatternItemsSingle&& other) = default;
    TuplePatternItemsSingle& operator=(TuplePatternItemsSingle&& other) =
default;

  protected:
    // Use covariance to implement clone function as returning this object
rather than base virtual TuplePatternItemsSingle*
clone_tuple_pattern_items_impl() const override { return new
TuplePatternItemsSingle(*this);
    }
};*/
// removed in favour of single-element TuplePatternItemsMultiple

// Class representing TuplePattern patterns where there are multiple patterns
class TuplePatternItemsMultiple : public TuplePatternItems
{
  std::vector<std::unique_ptr<Pattern>> patterns;

public:
  TuplePatternItemsMultiple (std::vector<std::unique_ptr<Pattern>> patterns)
    : patterns (std::move (patterns))
  {}

  // Copy constructor with vector clone
  TuplePatternItemsMultiple (TuplePatternItemsMultiple const &other)
  {
    patterns.reserve (other.patterns.size ());
    for (const auto &e : other.patterns)
      patterns.push_back (e->clone_pattern ());
  }

  // Overloaded assignment operator to vector clone
  TuplePatternItemsMultiple &operator= (TuplePatternItemsMultiple const &other)
  {
    patterns.reserve (other.patterns.size ());
    for (const auto &e : other.patterns)
      patterns.push_back (e->clone_pattern ());

    return *this;
  }

  // move constructors
  TuplePatternItemsMultiple (TuplePatternItemsMultiple &&other) = default;
  TuplePatternItemsMultiple &operator= (TuplePatternItemsMultiple &&other)
    = default;

  std::string as_string () const override;

  void accept_vis (ASTVisitor &vis) override;

  // TODO: seems kinda dodgy. Think of better way.
  std::vector<std::unique_ptr<Pattern>> &get_patterns () { return patterns; }
  const std::vector<std::unique_ptr<Pattern>> &get_patterns () const
  {
    return patterns;
  }

  TuplePatternItemType get_pattern_type () const override
  {
    return TuplePatternItemType::MULTIPLE;
  }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  TuplePatternItemsMultiple *clone_tuple_pattern_items_impl () const override
  {
    return new TuplePatternItemsMultiple (*this);
  }
};

// Class representing TuplePattern patterns where there are a range of patterns
class TuplePatternItemsRanged : public TuplePatternItems
{
  std::vector<std::unique_ptr<Pattern>> lower_patterns;
  std::vector<std::unique_ptr<Pattern>> upper_patterns;

public:
  TuplePatternItemsRanged (std::vector<std::unique_ptr<Pattern>> lower_patterns,
			   std::vector<std::unique_ptr<Pattern>> upper_patterns)
    : lower_patterns (std::move (lower_patterns)),
      upper_patterns (std::move (upper_patterns))
  {}

  // Copy constructor with vector clone
  TuplePatternItemsRanged (TuplePatternItemsRanged const &other)
  {
    lower_patterns.reserve (other.lower_patterns.size ());
    for (const auto &e : other.lower_patterns)
      lower_patterns.push_back (e->clone_pattern ());

    upper_patterns.reserve (other.upper_patterns.size ());
    for (const auto &e : other.upper_patterns)
      upper_patterns.push_back (e->clone_pattern ());
  }

  // Overloaded assignment operator to clone
  TuplePatternItemsRanged &operator= (TuplePatternItemsRanged const &other)
  {
    lower_patterns.reserve (other.lower_patterns.size ());
    for (const auto &e : other.lower_patterns)
      lower_patterns.push_back (e->clone_pattern ());

    upper_patterns.reserve (other.upper_patterns.size ());
    for (const auto &e : other.upper_patterns)
      upper_patterns.push_back (e->clone_pattern ());

    return *this;
  }

  // move constructors
  TuplePatternItemsRanged (TuplePatternItemsRanged &&other) = default;
  TuplePatternItemsRanged &operator= (TuplePatternItemsRanged &&other)
    = default;

  std::string as_string () const override;

  void accept_vis (ASTVisitor &vis) override;

  // TODO: seems kinda dodgy. Think of better way.
  std::vector<std::unique_ptr<Pattern>> &get_lower_patterns ()
  {
    return lower_patterns;
  }
  const std::vector<std::unique_ptr<Pattern>> &get_lower_patterns () const
  {
    return lower_patterns;
  }

  // TODO: seems kinda dodgy. Think of better way.
  std::vector<std::unique_ptr<Pattern>> &get_upper_patterns ()
  {
    return upper_patterns;
  }
  const std::vector<std::unique_ptr<Pattern>> &get_upper_patterns () const
  {
    return upper_patterns;
  }

  TuplePatternItemType get_pattern_type () const override
  {
    return TuplePatternItemType::RANGED;
  }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  TuplePatternItemsRanged *clone_tuple_pattern_items_impl () const override
  {
    return new TuplePatternItemsRanged (*this);
  }
};

// AST node representing a tuple pattern
class TuplePattern : public Pattern
{
  // bool has_tuple_pattern_items;
  std::unique_ptr<TuplePatternItems> items;
  Location locus;
  NodeId node_id;

public:
  std::string as_string () const override;

  // Returns true if the tuple pattern has items
  bool has_tuple_pattern_items () const { return items != nullptr; }

  TuplePattern (std::unique_ptr<TuplePatternItems> items, Location locus)
    : items (std::move (items)), locus (locus),
      node_id (Analysis::Mappings::get ()->get_next_node_id ())
  {}

  // Copy constructor requires clone
  TuplePattern (TuplePattern const &other) : locus (other.locus)
  {
    // guard to prevent null dereference
    node_id = other.node_id;
    if (other.items != nullptr)
      items = other.items->clone_tuple_pattern_items ();
  }

  // Overload assignment operator to clone
  TuplePattern &operator= (TuplePattern const &other)
  {
    locus = other.locus;
    node_id = other.node_id;

    // guard to prevent null dereference
    if (other.items != nullptr)
      items = other.items->clone_tuple_pattern_items ();
    else
      items = nullptr;

    return *this;
  }

  Location get_locus () const override final { return locus; }

  void accept_vis (ASTVisitor &vis) override;

  // TODO: seems kinda dodgy. Think of better way.
  std::unique_ptr<TuplePatternItems> &get_items ()
  {
    rust_assert (has_tuple_pattern_items ());
    return items;
  }

  NodeId get_node_id () const { return node_id; }

  NodeId get_pattern_node_id () const override final { return node_id; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  TuplePattern *clone_pattern_impl () const override
  {
    return new TuplePattern (*this);
  }
};

// AST node representing a pattern in parentheses, used to control precedence
class GroupedPattern : public Pattern
{
  std::unique_ptr<Pattern> pattern_in_parens;
  Location locus;
  NodeId node_id;

public:
  std::string as_string () const override
  {
    return "(" + pattern_in_parens->as_string () + ")";
  }

  GroupedPattern (std::unique_ptr<Pattern> pattern_in_parens, Location locus)
    : pattern_in_parens (std::move (pattern_in_parens)), locus (locus),
      node_id (Analysis::Mappings::get ()->get_next_node_id ())
  {}

  // Copy constructor uses clone
  GroupedPattern (GroupedPattern const &other)
    : pattern_in_parens (other.pattern_in_parens->clone_pattern ()),
      locus (other.locus), node_id (other.node_id)
  {}

  // Overload assignment operator to clone
  GroupedPattern &operator= (GroupedPattern const &other)
  {
    pattern_in_parens = other.pattern_in_parens->clone_pattern ();
    locus = other.locus;
    node_id = other.node_id;

    return *this;
  }

  // default move semantics
  GroupedPattern (GroupedPattern &&other) = default;
  GroupedPattern &operator= (GroupedPattern &&other) = default;

  Location get_locus () const override final { return locus; }

  void accept_vis (ASTVisitor &vis) override;

  // TODO: seems kinda dodgy. Think of better way.
  std::unique_ptr<Pattern> &get_pattern_in_parens ()
  {
    rust_assert (pattern_in_parens != nullptr);
    return pattern_in_parens;
  }

  NodeId get_node_id () const { return node_id; }

  NodeId get_pattern_node_id () const override final { return node_id; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  GroupedPattern *clone_pattern_impl () const override
  {
    return new GroupedPattern (*this);
  }
};

// AST node representing patterns that can match slices and arrays
class SlicePattern : public Pattern
{
  std::vector<std::unique_ptr<Pattern>> items;
  Location locus;
  NodeId node_id;

public:
  std::string as_string () const override;

  SlicePattern (std::vector<std::unique_ptr<Pattern>> items, Location locus)
    : items (std::move (items)), locus (locus),
      node_id (Analysis::Mappings::get ()->get_next_node_id ())
  {}

  // Copy constructor with vector clone
  SlicePattern (SlicePattern const &other) : locus (other.locus)
  {
    node_id = other.node_id;
    items.reserve (other.items.size ());
    for (const auto &e : other.items)
      items.push_back (e->clone_pattern ());
  }

  // Overloaded assignment operator to vector clone
  SlicePattern &operator= (SlicePattern const &other)
  {
    locus = other.locus;
    node_id = other.node_id;

    items.reserve (other.items.size ());
    for (const auto &e : other.items)
      items.push_back (e->clone_pattern ());

    return *this;
  }

  // move constructors
  SlicePattern (SlicePattern &&other) = default;
  SlicePattern &operator= (SlicePattern &&other) = default;

  Location get_locus () const override final { return locus; }

  void accept_vis (ASTVisitor &vis) override;

  // TODO: seems kinda dodgy. Think of better way.
  std::vector<std::unique_ptr<Pattern>> &get_items () { return items; }
  const std::vector<std::unique_ptr<Pattern>> &get_items () const
  {
    return items;
  }

  NodeId get_node_id () const { return node_id; }

  NodeId get_pattern_node_id () const override final { return node_id; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  SlicePattern *clone_pattern_impl () const override
  {
    return new SlicePattern (*this);
  }
};

// AST node for alternate patterns
// joins together what are technically 'PatternNoTopAlt's
class AltPattern : public Pattern
{
  std::vector<std::unique_ptr<Pattern>> alts;
  Location locus;
  NodeId node_id;

public:
  std::string as_string () const override;

  AltPattern (std::vector<std::unique_ptr<Pattern>> alts, Location locus)
    : alts (std::move (alts)), locus (locus),
      node_id (Analysis::Mappings::get ()->get_next_node_id ())
  {}

  // Copy constructor with vector clone
  AltPattern (AltPattern const &other) : locus (other.locus)
  {
    node_id = other.node_id;
    alts.reserve (other.alts.size ());
    for (const auto &e : other.alts)
      alts.push_back (e->clone_pattern ());
  }

  // Overloaded assignment operator to vector clone
  AltPattern &operator= (AltPattern const &other)
  {
    locus = other.locus;
    node_id = other.node_id;

    alts.reserve (other.alts.size ());
    for (const auto &e : other.alts)
      alts.push_back (e->clone_pattern ());

    return *this;
  }

  // move constructors
  AltPattern (AltPattern &&other) = default;
  AltPattern &operator= (AltPattern &&other) = default;

  Location get_locus () const override final { return locus; }

  void accept_vis (ASTVisitor &vis) override;

  // TODO: seems kinda dodgy. Think of better way.
  std::vector<std::unique_ptr<Pattern>> &get_alts () { return alts; }
  const std::vector<std::unique_ptr<Pattern>> &get_alts () const
  {
    return alts;
  }

  NodeId get_node_id () const { return node_id; }

  NodeId get_pattern_node_id () const override final { return node_id; }

protected:
  /* Use covariance to implement clone function as returning this object rather
   * than base */
  AltPattern *clone_pattern_impl () const override
  {
    return new AltPattern (*this);
  }
};

// Moved definition to rust-path.h
class PathPattern;

// Forward decls for paths (defined in rust-path.h)
class PathInExpression;
class QualifiedPathInExpression;

// Replaced with forward decl - defined in rust-macro.h
class MacroInvocation;
} // namespace AST
} // namespace Rust

#endif