// Copyright (C) 2020-2025 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_RESOLVE_PATTERN_H
#define RUST_AST_RESOLVE_PATTERN_H

#include "rust-ast-resolve-base.h"
#include "rust-ast-full.h"

namespace Rust {
namespace Resolver {

// Specifies whether the set of already bound patterns are related by 'Or' or
// 'Product'. Used to check for multiple bindings to the same identifier.
enum PatternBoundCtx
{
  // A product pattern context (e.g. struct and tuple patterns)
  Product,
  // An or-pattern context (e.g. p_0 | p_1 | ...)
  Or,
};

struct PatternBinding
{
  PatternBoundCtx ctx;
  std::set<Identifier> idents;

  PatternBinding (PatternBoundCtx ctx, std::set<Identifier> idents)
    : ctx (ctx), idents (idents)
  {}
};

// Info that gets stored in the map. Helps us detect if two bindings to the same
// identifier have different mutability or ref states.
class BindingTypeInfo
{
  Mutability mut;
  bool is_ref;
  location_t locus;

public:
  BindingTypeInfo (Mutability mut, bool is_ref, location_t locus)
    : mut (mut), is_ref (is_ref), locus (locus)
  {}

  BindingTypeInfo (BindingTypeInfo const &other)
    : mut (other.mut), is_ref (other.is_ref), locus (other.get_locus ())
  {}

  BindingTypeInfo (){};

  location_t get_locus () const { return locus; }
  Mutability get_mut () const { return mut; }
  bool get_is_ref () const { return is_ref; }

  BindingTypeInfo operator= (BindingTypeInfo const &other)
  {
    mut = other.mut;
    is_ref = other.is_ref;
    locus = other.get_locus ();

    return *this;
  }

  bool operator== (BindingTypeInfo const &other)
  {
    return mut == other.mut && is_ref == other.is_ref;
  }

  bool operator!= (BindingTypeInfo const &other)
  {
    return !BindingTypeInfo::operator== (other);
  }
};

typedef std::map<Identifier, BindingTypeInfo> BindingMap;

class PatternDeclaration : public ResolverBase
{
  using Rust::Resolver::ResolverBase::visit;

public:
  static void go (AST::Pattern &pattern, Rib::ItemType type);
  static void go (AST::Pattern &pattern, Rib::ItemType type,
		  std::vector<PatternBinding> &bindings);

  void visit (AST::IdentifierPattern &pattern) override;
  void visit (AST::GroupedPattern &pattern) override;
  void visit (AST::ReferencePattern &pattern) override;
  void visit (AST::PathInExpression &pattern) override;
  void visit (AST::StructPattern &pattern) override;
  void visit (AST::TupleStructPattern &pattern) override;
  void visit (AST::TuplePattern &pattern) override;
  void visit (AST::RangePattern &pattern) override;
  void visit (AST::AltPattern &pattern) override;
  void visit (AST::SlicePattern &pattern) override;

  void add_new_binding (Identifier ident, NodeId node_id, BindingTypeInfo info);
  void check_bindings_consistency (std::vector<BindingMap> &binding_maps);

private:
  PatternDeclaration (std::vector<PatternBinding> &bindings_with_ctx,
		      Rib::ItemType type)
    : ResolverBase (), bindings_with_ctx (bindings_with_ctx), type (type)
  {}

  // To avoid having a separate visitor for consistency checks, we store
  // bindings in two forms:

  // 1) Bindings as a vector of context-related sets.
  // Used for checking multiple bindings to the same identifier (i.e. E0415,
  // E0416).
  std::vector<PatternBinding> &bindings_with_ctx;

  // 2) Bindings as a map between identifiers and binding info.
  // Used for checking consistency between alt patterns (i.e. E0408, E0409).
  BindingMap binding_info_map;

  // we need to insert the missing and inconsistent bindings (found in
  // check_bindings_consistency) into maps to avoid duplication of error
  // messages.
  BindingMap inconsistent_bindings;
  BindingMap missing_bindings;

  Rib::ItemType type;
};

} // namespace Resolver
} // namespace Rust

#endif // RUST_AST_RESOLVE_PATTERN_H