// 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_HIR_PATTERN_H
#define RUST_HIR_PATTERN_H

#include "rust-common.h"
#include "rust-hir.h"

namespace Rust {
namespace HIR {

// Literal pattern HIR node (comparing to a literal)
class LiteralPattern : public Pattern
{
  Literal lit;
  Location locus;
  Analysis::NodeMapping mappings;

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

  // Constructor for a literal pattern
  LiteralPattern (Analysis::NodeMapping mappings, Literal lit, Location locus)
    : lit (std::move (lit)), locus (locus), mappings (mappings)
  {}

  LiteralPattern (Analysis::NodeMapping mappings, std::string val,
		  Literal::LitType type, Location locus)
    : lit (Literal (std::move (val), type, PrimitiveCoreType::CORETYPE_STR)),
      locus (locus), mappings (mappings)
  {}

  Location get_locus () const override { return locus; }

  void accept_vis (HIRFullVisitor &vis) override;
  void accept_vis (HIRPatternVisitor &vis) override;

  Analysis::NodeMapping get_pattern_mappings () const override final
  {
    return mappings;
  }

  PatternType get_pattern_type () const override final
  {
    return PatternType::LITERAL;
  }

  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 HIR node (bind value matched to a variable)
class IdentifierPattern : public Pattern
{
  Identifier variable_ident;
  bool is_ref;
  Mutability mut;
  std::unique_ptr<Pattern> to_bind;
  Location locus;
  Analysis::NodeMapping mappings;

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 (Analysis::NodeMapping mappings, Identifier ident,
		     Location locus, bool is_ref = false,
		     Mutability mut = Mutability::Imm,
		     std::unique_ptr<Pattern> to_bind = nullptr)
    : variable_ident (std::move (ident)), is_ref (is_ref), mut (mut),
      to_bind (std::move (to_bind)), locus (locus), mappings (mappings)
  {}

  // Copy constructor with clone
  IdentifierPattern (IdentifierPattern const &other)
    : variable_ident (other.variable_ident), is_ref (other.is_ref),
      mut (other.mut), locus (other.locus), mappings (other.mappings)
  {
    // 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;
    mut = other.mut;
    locus = other.locus;
    mappings = other.mappings;

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

    return *this;
  }

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

  Location get_locus () const override { return locus; }

  bool is_mut () const { return mut == Mutability::Mut; }

  void accept_vis (HIRFullVisitor &vis) override;
  void accept_vis (HIRPatternVisitor &vis) override;

  Analysis::NodeMapping get_pattern_mappings () const override final
  {
    return mappings;
  }

  Identifier get_identifier () const { return variable_ident; }

  PatternType get_pattern_type () const override final
  {
    return PatternType::IDENTIFIER;
  }

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

// HIR node for using the '_' wildcard "match any value" pattern
class WildcardPattern : public Pattern
{
  Location locus;
  Analysis::NodeMapping mappings;

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

  WildcardPattern (Analysis::NodeMapping mappings, Location locus)
    : locus (locus), mappings (mappings)
  {}

  Location get_locus () const override { return locus; }

  void accept_vis (HIRFullVisitor &vis) override;
  void accept_vis (HIRPatternVisitor &vis) override;

  Analysis::NodeMapping get_pattern_mappings () const override final
  {
    return mappings;
  }

  PatternType get_pattern_type () const override final
  {
    return PatternType::WILDCARD;
  }

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 (HIRFullVisitor &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;

  Location get_locus () const { return locus; }

  Literal get_literal () const { return literal; }

  void accept_vis (HIRFullVisitor &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 (); }

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

  void accept_vis (HIRFullVisitor &vis) override;

  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 (HIRFullVisitor &vis) override;

  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);
  }
};

// HIR 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;
  Analysis::NodeMapping mappings;

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

  // Constructor
  RangePattern (Analysis::NodeMapping mappings,
		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),
      mappings (mappings)
  {}

  // 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),
      mappings (other.mappings)
  {}

  // 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;
    mappings = other.mappings;

    return *this;
  }

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

  Location get_locus () const override { return locus; }

  void accept_vis (HIRFullVisitor &vis) override;
  void accept_vis (HIRPatternVisitor &vis) override;

  Analysis::NodeMapping get_pattern_mappings () const override final
  {
    return mappings;
  }

  PatternType get_pattern_type () const override final
  {
    return PatternType::RANGE;
  }

  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;
  }

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

// HIR node for pattern based on dereferencing the pointers given
class ReferencePattern : public Pattern
{
  Mutability mut;
  std::unique_ptr<Pattern> pattern;
  Location locus;
  Analysis::NodeMapping mappings;

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

  ReferencePattern (Analysis::NodeMapping mappings,
		    std::unique_ptr<Pattern> pattern, Mutability reference_mut,
		    Location locus)
    : mut (reference_mut), pattern (std::move (pattern)), locus (locus),
      mappings (mappings)
  {}

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

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

    return *this;
  }

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

  bool is_mut () const { return mut == Mutability::Mut; }

  void accept_vis (HIRFullVisitor &vis) override;
  void accept_vis (HIRPatternVisitor &vis) override;

  Analysis::NodeMapping get_pattern_mappings () const override final
  {
    return mappings;
  }

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

  PatternType get_pattern_type () const override final
  {
    return PatternType::REFERENCE;
  }

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

// Base class for a single field in a struct pattern - abstract
class StructPatternField
{
  AST::AttrVec outer_attrs;
  Location locus;
  Analysis::NodeMapping mappings;

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;
  virtual void accept_vis (HIRFullVisitor &vis) = 0;
  virtual ItemType get_item_type () const = 0;

  Location get_locus () const { return locus; }
  Analysis::NodeMapping get_mappings () const { return mappings; };

protected:
  StructPatternField (Analysis::NodeMapping mappings,
		      AST::AttrVec outer_attribs, Location locus)
    : outer_attrs (std::move (outer_attribs)), locus (locus),
      mappings (mappings)
  {}

  // 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 (Analysis::NodeMapping mappings, TupleIndex index,
			      std::unique_ptr<Pattern> tuple_pattern,
			      AST::AttrVec outer_attribs, Location locus)
    : StructPatternField (mappings, std::move (outer_attribs), locus),
      index (index), tuple_pattern (std::move (tuple_pattern))
  {}

  // Copy constructor requires clone
  StructPatternFieldTuplePat (StructPatternFieldTuplePat const &other)
    : StructPatternField (other), index (other.index),
      tuple_pattern (other.tuple_pattern->clone_pattern ())
  {}

  // Overload assignment operator to perform clone
  StructPatternFieldTuplePat &
  operator= (StructPatternFieldTuplePat const &other)
  {
    StructPatternField::operator= (other);
    tuple_pattern = other.tuple_pattern->clone_pattern ();
    index = other.index;
    // outer_attrs = other.outer_attrs;

    return *this;
  }

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

  std::string as_string () const override;

  void accept_vis (HIRFullVisitor &vis) override;

  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 (Analysis::NodeMapping mappings, Identifier ident,
			      std::unique_ptr<Pattern> ident_pattern,
			      AST::AttrVec outer_attrs, Location locus)
    : StructPatternField (mappings, std::move (outer_attrs), locus),
      ident (std::move (ident)), ident_pattern (std::move (ident_pattern))
  {}

  // Copy constructor requires clone
  StructPatternFieldIdentPat (StructPatternFieldIdentPat const &other)
    : StructPatternField (other), ident (other.ident),
      ident_pattern (other.ident_pattern->clone_pattern ())
  {}

  // Overload assignment operator to clone
  StructPatternFieldIdentPat &
  operator= (StructPatternFieldIdentPat const &other)
  {
    StructPatternField::operator= (other);
    ident = other.ident;
    ident_pattern = other.ident_pattern->clone_pattern ();
    // outer_attrs = other.outer_attrs;

    return *this;
  }

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

  std::string as_string () const override;

  void accept_vis (HIRFullVisitor &vis) override;

  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;
  Mutability mut;
  Identifier ident;

public:
  StructPatternFieldIdent (Analysis::NodeMapping mappings, Identifier ident,
			   bool is_ref, Mutability mut,
			   AST::AttrVec outer_attrs, Location locus)
    : StructPatternField (mappings, std::move (outer_attrs), locus),
      has_ref (is_ref), mut (mut), ident (std::move (ident))
  {}

  std::string as_string () const override;

  bool is_mut () const { return mut == Mutability::Mut; }

  void accept_vis (HIRFullVisitor &vis) override;

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

  Identifier get_identifier () const { return ident; };

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:
  std::vector<std::unique_ptr<StructPatternField> > fields;

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 (); }

  // Constructor for StructPatternElements with both (potentially)
  StructPatternElements (
    std::vector<std::unique_ptr<StructPatternField> > fields)
    : fields (std::move (fields))
  {}

  // Copy constructor with vector clone
  StructPatternElements (StructPatternElements const &other)
  {
    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)
  {
    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;

  std::vector<std::unique_ptr<StructPatternField> > &
  get_struct_pattern_fields ()
  {
    return fields;
  }
};

// Struct pattern HIR node representation
class StructPattern : public Pattern
{
  PathInExpression path;
  StructPatternElements elems;
  Analysis::NodeMapping mappings;

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

  StructPattern (Analysis::NodeMapping mappings, PathInExpression struct_path,
		 StructPatternElements elems)
    : path (std::move (struct_path)), elems (std::move (elems)),
      mappings (mappings)
  {}

  bool has_struct_pattern_elems () const { return !elems.is_empty (); }

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

  void accept_vis (HIRFullVisitor &vis) override;
  void accept_vis (HIRPatternVisitor &vis) override;

  PathInExpression &get_path () { return path; }
  StructPatternElements &get_struct_pattern_elems () { return elems; }

  Analysis::NodeMapping get_pattern_mappings () const override final
  {
    return mappings;
  }

  PatternType get_pattern_type () const override final
  {
    return PatternType::STRUCT;
  }

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 (HIRFullVisitor &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 (HIRFullVisitor &vis) override;

  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 (HIRFullVisitor &vis) override;

  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);
  }
};

// HIR node representing a tuple struct pattern
class TupleStructPattern : public Pattern
{
  PathInExpression path;
  std::unique_ptr<TupleStructItems> items;
  Analysis::NodeMapping mappings;

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

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

  TupleStructPattern (Analysis::NodeMapping mappings,
		      PathInExpression tuple_struct_path,
		      std::unique_ptr<TupleStructItems> items)
    : path (std::move (tuple_struct_path)), items (std::move (items)),
      mappings (mappings)
  {}

  // Copy constructor required to clone
  TupleStructPattern (TupleStructPattern const &other)
    : path (other.path), items (other.items->clone_tuple_struct_items ()),
      mappings (other.mappings)
  {}

  // Operator overload assignment operator to clone
  TupleStructPattern &operator= (TupleStructPattern const &other)
  {
    path = other.path;
    items = other.items->clone_tuple_struct_items ();
    mappings = other.mappings;

    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 (HIRFullVisitor &vis) override;
  void accept_vis (HIRPatternVisitor &vis) override;

  PathInExpression &get_path () { return path; }

  std::unique_ptr<TupleStructItems> &get_items () { return items; }

  Analysis::NodeMapping get_pattern_mappings () const override final
  {
    return mappings;
  }

  PatternType get_pattern_type () const override final
  {
    return PatternType::TUPLE_STRUCT;
  }

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 (HIRFullVisitor &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 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 (HIRFullVisitor &vis) override;

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

  std::vector<std::unique_ptr<Pattern> > &get_patterns () { return patterns; }
  const std::vector<std::unique_ptr<Pattern> > &get_patterns () const
  {
    return patterns;
  }

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 (HIRFullVisitor &vis) override;

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

  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;
  }

  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;
  }

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);
  }
};

// HIR node representing a tuple pattern
class TuplePattern : public Pattern
{
  std::unique_ptr<TuplePatternItems> items;
  Location locus;
  Analysis::NodeMapping mappings;

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 (Analysis::NodeMapping mappings,
		std::unique_ptr<TuplePatternItems> items, Location locus)
    : items (std::move (items)), locus (locus), mappings (mappings)
  {}

  // Copy constructor requires clone
  TuplePattern (TuplePattern const &other)
    : items (other.items->clone_tuple_pattern_items ()), locus (other.locus),
      mappings (other.mappings)
  {}

  // Overload assignment operator to clone
  TuplePattern &operator= (TuplePattern const &other)
  {
    items = other.items->clone_tuple_pattern_items ();
    locus = other.locus;
    mappings = other.mappings;

    return *this;
  }

  Location get_locus () const override { return locus; }

  void accept_vis (HIRFullVisitor &vis) override;
  void accept_vis (HIRPatternVisitor &vis) override;

  Analysis::NodeMapping get_pattern_mappings () const override final
  {
    return mappings;
  }

  PatternType get_pattern_type () const override final
  {
    return PatternType::TUPLE;
  }

  std::unique_ptr<TuplePatternItems> &get_items () { return items; }
  const std::unique_ptr<TuplePatternItems> &get_items () const { return items; }

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

// HIR node representing patterns that can match slices and arrays
class SlicePattern : public Pattern
{
  std::vector<std::unique_ptr<Pattern> > items;
  Location locus;
  Analysis::NodeMapping mappings;

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

  SlicePattern (Analysis::NodeMapping mappings,
		std::vector<std::unique_ptr<Pattern> > items, Location locus)
    : items (std::move (items)), locus (locus), mappings (mappings)
  {}

  // Copy constructor with vector clone
  SlicePattern (SlicePattern const &other)
    : locus (other.locus), mappings (other.mappings)
  {
    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;
    mappings = other.mappings;

    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 { return locus; }

  void accept_vis (HIRFullVisitor &vis) override;
  void accept_vis (HIRPatternVisitor &vis) override;

  Analysis::NodeMapping get_pattern_mappings () const override final
  {
    return mappings;
  }

  PatternType get_pattern_type () const override final
  {
    return PatternType::SLICE;
  }

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

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

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

} // namespace HIR
} // namespace Rust

#endif
