// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_PARSING_EXPRESSION_SCOPE_H_
#define V8_PARSING_EXPRESSION_SCOPE_H_

#include <utility>

#include "src/ast/scopes.h"
#include "src/common/message-template.h"
#include "src/objects/function-kind.h"
#include "src/parsing/scanner.h"
#include "src/zone/zone.h"  // For ScopedPtrList.

namespace v8 {
namespace internal {

template <typename Types>
class ExpressionParsingScope;
template <typename Types>
class AccumulationScope;
template <typename Types>
class ArrowHeadParsingScope;
template <typename Types>
class ParameterDeclarationParsingScope;
template <typename Types>
class VariableDeclarationParsingScope;
class VariableProxy;

// ExpressionScope is used in a stack fashion, and is used to specialize
// expression parsing for the task at hand. It allows the parser to reuse the
// same code to parse destructuring declarations, assignment patterns,
// expressions, and (async) arrow function heads.
//
// One of the specific subclasses needs to be instantiated to tell the parser
// the meaning of the expression it will parse next. The parser then calls
// Record* on the expression_scope() to indicate errors. The expression_scope
// will either discard those errors, immediately report those errors, or
// classify the errors for later validation.
// TODO(verwaest): Record is a slightly odd name since it will directly throw
// for unambiguous scopes.
template <typename Types>
class ExpressionScope {
 public:
  ExpressionScope(const ExpressionScope&) = delete;
  ExpressionScope& operator=(const ExpressionScope&) = delete;

  using ParserT = typename Types::Impl;
  using ExpressionT = typename Types::Expression;

  VariableProxy* NewVariable(const AstRawString* name,
                             int pos = kNoSourcePosition) {
    VariableProxy* result = parser_->NewRawVariable(name, pos);
    if (CanBeExpression()) {
      AsExpressionParsingScope()->TrackVariable(result);
    } else {
      Variable* var = Declare(name, pos);
      if (IsVarDeclaration()) {
        bool passed_through_with = false;
        for (Scope* scope = parser()->scope(); !scope->is_declaration_scope();
             scope = scope->outer_scope()) {
          if (scope->is_with_scope()) {
            passed_through_with = true;
          } else if (scope->is_catch_scope()) {
            Variable* var = scope->LookupLocal(name);
            // If a variable is declared in a catch scope with a masking
            // catch-declared variable, the initializing assignment is an
            // assignment to the catch-declared variable instead.
            // https://tc39.es/ecma262/#sec-variablestatements-in-catch-blocks
            if (var != nullptr) {
              result->set_is_assigned();
              if (passed_through_with) break;
              result->BindTo(var);
              var->SetMaybeAssigned();
              return result;
            }
          }
        }
        if (passed_through_with) {
          // If a variable is declared in a with scope, the initializing
          // assignment might target a with-declared variable instead.
          parser()->scope()->AddUnresolved(result);
          return result;
        }
      }
      DCHECK_NOT_NULL(var);
      result->BindTo(var);
    }
    return result;
  }

  void MergeVariableList(
      ScopedList<std::pair<VariableProxy*, int>>* variable_list) {
    if (!CanBeExpression()) return;
    // Merged variables come from a CanBeDeclaration expression scope, and
    // weren't added as unresolved references to the variable scope yet. Add
    // them to the variable scope on the boundary where it becomes clear they
    // aren't declarations. We explicitly delay declaring the variables up to
    // that point to avoid trying to add them to the unresolved list multiple
    // times, e.g., for (((a))).
    if (!CanBeDeclaration()) {
      for (auto& proxy_initializer_pair : *variable_list) {
        VariableProxy* proxy = proxy_initializer_pair.first;
        this->parser()->scope()->AddUnresolved(proxy);
      }
    }
    variable_list->MergeInto(AsExpressionParsingScope()->variable_list());
  }

  Variable* Declare(const AstRawString* name, int pos = kNoSourcePosition) {
    if (type_ == kParameterDeclaration) {
      return AsParameterDeclarationParsingScope()->Declare(name, pos);
    }
    return AsVariableDeclarationParsingScope()->Declare(name, pos);
  }

  void MarkIdentifierAsAssigned() {
    if (!CanBeExpression()) return;
    AsExpressionParsingScope()->MarkIdentifierAsAssigned();
  }

  void ValidateAsPattern(ExpressionT expression, int begin, int end) {
    if (!CanBeExpression()) return;
    AsExpressionParsingScope()->ValidatePattern(expression, begin, end);
    AsExpressionParsingScope()->ClearExpressionError();
  }

  void ValidateAsExpression() {
    if (!CanBeExpression()) return;
    AsExpressionParsingScope()->ValidateExpression();
    AsExpressionParsingScope()->ClearPatternError();
  }

  // Record async arrow parameters errors in all ambiguous async arrow scopes in
  // the chain up to the first unambiguous scope.
  void RecordAsyncArrowParametersError(const Scanner::Location& loc,
                                       MessageTemplate message) {
    // Only ambiguous scopes (ExpressionParsingScope, *ArrowHeadParsingScope)
    // need to propagate errors to a possible kAsyncArrowHeadParsingScope, so
    // immediately return if the current scope is not ambiguous.
    if (!CanBeExpression()) return;
    AsExpressionParsingScope()->RecordAsyncArrowParametersError(loc, message);
  }

  // Record initializer errors in all scopes that can turn into parameter scopes
  // (ArrowHeadParsingScopes) up to the first known unambiguous parameter scope.
  void RecordParameterInitializerError(const Scanner::Location& loc,
                                       MessageTemplate message) {
    ExpressionScope* scope = this;
    while (!scope->IsCertainlyParameterDeclaration()) {
      if (!has_possible_parameter_in_scope_chain_) return;
      if (scope->CanBeParameterDeclaration()) {
        scope->AsArrowHeadParsingScope()->RecordDeclarationError(loc, message);
      }
      scope = scope->parent();
      if (scope == nullptr) return;
    }
    Report(loc, message);
  }

  void RecordThisUse() {
    ExpressionScope* scope = this;
    do {
      if (scope->IsArrowHeadParsingScope()) {
        scope->AsArrowHeadParsingScope()->RecordThisUse();
      }
      scope = scope->parent();
    } while (scope != nullptr);
  }

  void RecordPatternError(const Scanner::Location& loc,
                          MessageTemplate message) {
    // TODO(verwaest): Non-assigning expression?
    if (IsCertainlyPattern()) {
      Report(loc, message);
    } else {
      AsExpressionParsingScope()->RecordPatternError(loc, message);
    }
  }

  void RecordStrictModeParameterError(const Scanner::Location& loc,
                                      MessageTemplate message) {
    DCHECK_IMPLIES(!has_error(), loc.IsValid());
    if (!CanBeParameterDeclaration()) return;
    if (IsCertainlyParameterDeclaration()) {
      if (is_strict(parser_->language_mode())) {
        Report(loc, message);
      } else {
        parser_->parameters_->set_strict_parameter_error(loc, message);
      }
    } else {
      parser_->next_arrow_function_info_.strict_parameter_error_location = loc;
      parser_->next_arrow_function_info_.strict_parameter_error_message =
          message;
    }
  }

  void RecordDeclarationError(const Scanner::Location& loc,
                              MessageTemplate message) {
    if (!CanBeDeclaration()) return;
    if (IsCertainlyDeclaration()) {
      Report(loc, message);
    } else {
      AsArrowHeadParsingScope()->RecordDeclarationError(loc, message);
    }
  }

  void RecordExpressionError(const Scanner::Location& loc,
                             MessageTemplate message) {
    if (!CanBeExpression()) return;
    // TODO(verwaest): Non-assigning expression?
    // if (IsCertainlyExpression()) Report(loc, message);
    AsExpressionParsingScope()->RecordExpressionError(loc, message);
  }

  void RecordNonSimpleParameter() {
    if (!IsArrowHeadParsingScope()) return;
    AsArrowHeadParsingScope()->RecordNonSimpleParameter();
  }

  bool IsCertainlyDeclaration() const {
    return base::IsInRange(type_, kParameterDeclaration, kLexicalDeclaration);
  }

  int SetInitializers(int variable_index, int peek_position) {
    if (CanBeExpression()) {
      return AsExpressionParsingScope()->SetInitializers(variable_index,
                                                         peek_position);
    }
    return variable_index;
  }

  bool has_possible_arrow_parameter_in_scope_chain() const {
    return has_possible_arrow_parameter_in_scope_chain_;
  }

 protected:
  enum ScopeType : uint8_t {
    // Expression or assignment target.
    kExpression,

    // Declaration or expression or assignment target.
    kMaybeArrowParameterDeclaration,
    kMaybeAsyncArrowParameterDeclaration,

    // Declarations.
    kParameterDeclaration,
    kVarDeclaration,
    kLexicalDeclaration,
  };

  ParserT* parser() const { return parser_; }
  ExpressionScope* parent() const { return parent_; }

  void Report(const Scanner::Location& loc, MessageTemplate message) const {
    parser_->ReportMessageAt(loc, message);
  }

  ExpressionScope(ParserT* parser, ScopeType type)
      : parser_(parser),
        parent_(parser->expression_scope_),
        type_(type),
        has_possible_parameter_in_scope_chain_(
            CanBeParameterDeclaration() ||
            (parent_ && parent_->has_possible_parameter_in_scope_chain_)),
        has_possible_arrow_parameter_in_scope_chain_(
            CanBeArrowParameterDeclaration() ||
            (parent_ &&
             parent_->has_possible_arrow_parameter_in_scope_chain_)) {
    parser->expression_scope_ = this;
  }

  ~ExpressionScope() {
    DCHECK(parser_->expression_scope_ == this ||
           parser_->expression_scope_ == parent_);
    parser_->expression_scope_ = parent_;
  }

  ExpressionParsingScope<Types>* AsExpressionParsingScope() {
    DCHECK(CanBeExpression());
    return static_cast<ExpressionParsingScope<Types>*>(this);
  }

#ifdef DEBUG
  bool has_error() const { return parser_->has_error(); }
#endif

  bool CanBeExpression() const {
    return base::IsInRange(type_, kExpression,
                           kMaybeAsyncArrowParameterDeclaration);
  }
  bool CanBeDeclaration() const {
    return base::IsInRange(type_, kMaybeArrowParameterDeclaration,
                           kLexicalDeclaration);
  }
  bool IsVariableDeclaration() const {
    return base::IsInRange(type_, kVarDeclaration, kLexicalDeclaration);
  }
  bool IsLexicalDeclaration() const { return type_ == kLexicalDeclaration; }
  bool IsAsyncArrowHeadParsingScope() const {
    return type_ == kMaybeAsyncArrowParameterDeclaration;
  }
  bool IsVarDeclaration() const { return type_ == kVarDeclaration; }

 private:
  friend class AccumulationScope<Types>;
  friend class ExpressionParsingScope<Types>;

  ArrowHeadParsingScope<Types>* AsArrowHeadParsingScope() {
    DCHECK(IsArrowHeadParsingScope());
    return static_cast<ArrowHeadParsingScope<Types>*>(this);
  }

  ParameterDeclarationParsingScope<Types>*
  AsParameterDeclarationParsingScope() {
    DCHECK(IsCertainlyParameterDeclaration());
    return static_cast<ParameterDeclarationParsingScope<Types>*>(this);
  }

  VariableDeclarationParsingScope<Types>* AsVariableDeclarationParsingScope() {
    DCHECK(IsVariableDeclaration());
    return static_cast<VariableDeclarationParsingScope<Types>*>(this);
  }

  bool IsArrowHeadParsingScope() const {
    return base::IsInRange(type_, kMaybeArrowParameterDeclaration,
                           kMaybeAsyncArrowParameterDeclaration);
  }
  bool IsCertainlyPattern() const { return IsCertainlyDeclaration(); }
  bool CanBeParameterDeclaration() const {
    return base::IsInRange(type_, kMaybeArrowParameterDeclaration,
                           kParameterDeclaration);
  }
  bool CanBeArrowParameterDeclaration() const {
    return base::IsInRange(type_, kMaybeArrowParameterDeclaration,
                           kMaybeAsyncArrowParameterDeclaration);
  }
  bool IsCertainlyParameterDeclaration() const {
    return type_ == kParameterDeclaration;
  }

  ParserT* parser_;
  ExpressionScope<Types>* parent_;
  ScopeType type_;
  bool has_possible_parameter_in_scope_chain_;
  bool has_possible_arrow_parameter_in_scope_chain_;
};

// Used to unambiguously parse var, let, const declarations.
template <typename Types>
class VariableDeclarationParsingScope : public ExpressionScope<Types> {
 public:
  using ParserT = typename Types::Impl;
  using ExpressionScopeT = ExpressionScope<Types>;
  using ScopeType = typename ExpressionScopeT::ScopeType;

  VariableDeclarationParsingScope(ParserT* parser, VariableMode mode,
                                  ZonePtrList<const AstRawString>* names)
      : ExpressionScopeT(parser, IsLexicalVariableMode(mode)
                                     ? ExpressionScopeT::kLexicalDeclaration
                                     : ExpressionScopeT::kVarDeclaration),
        mode_(mode),
        names_(names) {}

  VariableDeclarationParsingScope(const VariableDeclarationParsingScope&) =
      delete;
  VariableDeclarationParsingScope& operator=(
      const VariableDeclarationParsingScope&) = delete;

  Variable* Declare(const AstRawString* name, int pos) {
    VariableKind kind = NORMAL_VARIABLE;
    bool was_added;
    Variable* var = this->parser()->DeclareVariable(
        name, kind, mode_, Variable::DefaultInitializationFlag(mode_),
        this->parser()->scope(), &was_added, pos);
    if (was_added &&
        this->parser()->scope()->num_var() > kMaxNumFunctionLocals) {
      this->parser()->ReportMessage(MessageTemplate::kTooManyVariables);
    }
    if (names_) names_->Add(name, this->parser()->zone());
    if (this->IsLexicalDeclaration()) {
      if (this->parser()->IsLet(name)) {
        this->parser()->ReportMessageAt(
            Scanner::Location(pos, pos + name->length()),
            MessageTemplate::kLetInLexicalBinding);
      }
    } else {
      if (this->parser()->loop_nesting_depth() > 0) {
        // Due to hoisting, the value of a 'var'-declared variable may actually
        // change even if the code contains only the "initial" assignment,
        // namely when that assignment occurs inside a loop.  For example:
        //
        //   let i = 10;
        //   do { var x = i } while (i--):
        //
        // Note that non-lexical variables include temporaries, which may also
        // get assigned inside a loop due to the various rewritings that the
        // parser performs.
        //
        // Pessimistically mark all vars in loops as assigned. This
        // overapproximates the actual assigned vars due to unassigned var
        // without initializer, but that's unlikely anyway.
        //
        // This also handles marking of loop variables in for-in and for-of
        // loops, as determined by loop-nesting-depth.
        DCHECK_NOT_NULL(var);
        var->SetMaybeAssigned();
      }
    }
    return var;
  }

 private:
  // Limit the allowed number of local variables in a function. The hard limit
  // in Ignition is 2^31-1 due to the size of register operands. We limit it to
  // a more reasonable lower up-limit.
  static const int kMaxNumFunctionLocals = (1 << 23) - 1;

  VariableMode mode_;
  ZonePtrList<const AstRawString>* names_;
};

template <typename Types>
class ParameterDeclarationParsingScope : public ExpressionScope<Types> {
 public:
  using ParserT = typename Types::Impl;
  using ExpressionScopeT = ExpressionScope<Types>;
  using ScopeType = typename ExpressionScopeT::ScopeType;

  explicit ParameterDeclarationParsingScope(ParserT* parser)
      : ExpressionScopeT(parser, ExpressionScopeT::kParameterDeclaration) {}

  ParameterDeclarationParsingScope(const ParameterDeclarationParsingScope&) =
      delete;
  ParameterDeclarationParsingScope& operator=(
      const ParameterDeclarationParsingScope&) = delete;

  Variable* Declare(const AstRawString* name, int pos) {
    VariableKind kind = PARAMETER_VARIABLE;
    VariableMode mode = VariableMode::kVar;
    bool was_added;
    Variable* var = this->parser()->DeclareVariable(
        name, kind, mode, Variable::DefaultInitializationFlag(mode),
        this->parser()->scope(), &was_added, pos);
    if (!has_duplicate() && !was_added) {
      duplicate_loc_ = Scanner::Location(pos, pos + name->length());
    }
    return var;
  }

  bool has_duplicate() const { return duplicate_loc_.IsValid(); }

  const Scanner::Location& duplicate_location() const { return duplicate_loc_; }

 private:
  Scanner::Location duplicate_loc_ = Scanner::Location::invalid();
};

// Parsing expressions is always ambiguous between at least left-hand-side and
// right-hand-side of assignments. This class is used to keep track of errors
// relevant for either side until it is clear what was being parsed.
// The class also keeps track of all variable proxies that are created while the
// scope was active. If the scope is an expression, the variable proxies will be
// added to the unresolved list. Otherwise they are declarations and aren't
// added. The list is also used to mark the variables as assigned in case we are
// parsing an assignment expression.
template <typename Types>
class ExpressionParsingScope : public ExpressionScope<Types> {
 public:
  using ParserT = typename Types::Impl;
  using ExpressionT = typename Types::Expression;
  using ExpressionScopeT = ExpressionScope<Types>;
  using ScopeType = typename ExpressionScopeT::ScopeType;

  explicit ExpressionParsingScope(
      ParserT* parser, ScopeType type = ExpressionScopeT::kExpression)
      : ExpressionScopeT(parser, type),
        variable_list_(parser->variable_buffer()),
        has_async_arrow_in_scope_chain_(
            type == ExpressionScopeT::kMaybeAsyncArrowParameterDeclaration ||
            (this->parent() && this->parent()->CanBeExpression() &&
             this->parent()
                 ->AsExpressionParsingScope()
                 ->has_async_arrow_in_scope_chain_)) {
    DCHECK(this->CanBeExpression());
    clear(kExpressionIndex);
    clear(kPatternIndex);
  }

  ExpressionParsingScope(const ExpressionParsingScope&) = delete;
  ExpressionParsingScope& operator=(const ExpressionParsingScope&) = delete;

  void RecordAsyncArrowParametersError(const Scanner::Location& loc,
                                       MessageTemplate message) {
    for (ExpressionScopeT* scope = this; scope != nullptr;
         scope = scope->parent()) {
      if (!has_async_arrow_in_scope_chain_) break;
      if (scope->type_ ==
          ExpressionScopeT::kMaybeAsyncArrowParameterDeclaration) {
        scope->AsArrowHeadParsingScope()->RecordDeclarationError(loc, message);
      }
    }
  }

  ~ExpressionParsingScope() { DCHECK(this->has_error() || verified_); }

  ExpressionT ValidateAndRewriteReference(ExpressionT expression, int beg_pos,
                                          int end_pos) {
    if (V8_LIKELY(this->parser()->IsAssignableIdentifier(expression))) {
      MarkIdentifierAsAssigned();
      this->mark_verified();
      return expression;
    } else if (V8_LIKELY(expression->IsProperty())) {
      ValidateExpression();
      return expression;
    }
    this->mark_verified();
    const bool early_error = false;
    return this->parser()->RewriteInvalidReferenceExpression(
        expression, beg_pos, end_pos, MessageTemplate::kInvalidLhsInFor,
        early_error);
  }

  void RecordExpressionError(const Scanner::Location& loc,
                             MessageTemplate message) {
    Record(kExpressionIndex, loc, message);
  }

  void RecordPatternError(const Scanner::Location& loc,
                          MessageTemplate message) {
    Record(kPatternIndex, loc, message);
  }

  void ValidateExpression() { Validate(kExpressionIndex); }

  void ValidatePattern(ExpressionT expression, int begin, int end) {
    Validate(kPatternIndex);
    if (expression->is_parenthesized()) {
      ExpressionScopeT::Report(Scanner::Location(begin, end),
                               MessageTemplate::kInvalidDestructuringTarget);
    }
    for (auto& variable_initializer_pair : variable_list_) {
      variable_initializer_pair.first->set_is_assigned();
    }
  }

  void ClearExpressionError() {
    DCHECK(verified_);
#ifdef DEBUG
    verified_ = false;
#endif
    clear(kExpressionIndex);
  }

  void ClearPatternError() {
    DCHECK(verified_);
#ifdef DEBUG
    verified_ = false;
#endif
    clear(kPatternIndex);
  }

  void TrackVariable(VariableProxy* variable) {
    if (!this->CanBeDeclaration()) {
      this->parser()->scope()->AddUnresolved(variable);
    }
    variable_list_.Add({variable, kNoSourcePosition});
  }

  void MarkIdentifierAsAssigned() {
    // It's possible we're parsing a syntax error. In that case it's not
    // guaranteed that there's a variable in the list.
    if (variable_list_.length() == 0) return;
    variable_list_.at(variable_list_.length() - 1).first->set_is_assigned();
  }

  int SetInitializers(int first_variable_index, int position) {
    int len = variable_list_.length();
    if (len == 0) return 0;

    int end = len - 1;
    // Loop backwards and abort as soon as we see one that's already set to
    // avoid a loop on expressions like a,b,c,d,e,f,g (outside of an arrowhead).
    // TODO(delphick): Look into removing this loop.
    for (int i = end; i >= first_variable_index &&
                      variable_list_.at(i).second == kNoSourcePosition;
         --i) {
      variable_list_.at(i).second = position;
    }
    return end;
  }

  ScopedList<std::pair<VariableProxy*, int>>* variable_list() {
    return &variable_list_;
  }

 protected:
  bool is_verified() const {
#ifdef DEBUG
    return verified_;
#else
    return false;
#endif
  }

  void ValidatePattern() { Validate(kPatternIndex); }

 private:
  friend class AccumulationScope<Types>;

  enum ErrorNumber : uint8_t {
    kExpressionIndex = 0,
    kPatternIndex = 1,
    kNumberOfErrors = 2,
  };
  void clear(int index) {
    messages_[index] = MessageTemplate::kNone;
    locations_[index] = Scanner::Location::invalid();
  }
  bool is_valid(int index) const { return !locations_[index].IsValid(); }
  void Record(int index, const Scanner::Location& loc,
              MessageTemplate message) {
    DCHECK_IMPLIES(!this->has_error(), loc.IsValid());
    if (!is_valid(index)) return;
    messages_[index] = message;
    locations_[index] = loc;
  }
  void Validate(int index) {
    DCHECK(!this->is_verified());
    if (!is_valid(index)) Report(index);
    this->mark_verified();
  }
  void Report(int index) const {
    ExpressionScopeT::Report(locations_[index], messages_[index]);
  }

  // Debug verification to make sure every scope is validated exactly once.
  void mark_verified() {
#ifdef DEBUG
    verified_ = true;
#endif
  }
  void clear_verified() {
#ifdef DEBUG
    verified_ = false;
#endif
  }
#ifdef DEBUG
  bool verified_ = false;
#endif

  ScopedList<std::pair<VariableProxy*, int>> variable_list_;
  MessageTemplate messages_[kNumberOfErrors];
  Scanner::Location locations_[kNumberOfErrors];
  bool has_async_arrow_in_scope_chain_;
};

// This class is used to parse multiple ambiguous expressions and declarations
// in the same scope. E.g., in async(X,Y,Z) or [X,Y,Z], X and Y and Z will all
// be parsed in the respective outer ArrowHeadParsingScope and
// ExpressionParsingScope. It provides a clean error state in the underlying
// scope to parse the individual expressions, while keeping track of the
// expression and pattern errors since the start. The AccumulationScope is only
// used to keep track of the errors so far, and the underlying ExpressionScope
// keeps being used as the expression_scope(). If the expression_scope() isn't
// ambiguous, this class does not do anything.
template <typename Types>
class AccumulationScope {
 public:
  using ParserT = typename Types::Impl;

  static const int kNumberOfErrors =
      ExpressionParsingScope<Types>::kNumberOfErrors;
  explicit AccumulationScope(ExpressionScope<Types>* scope) : scope_(nullptr) {
    if (!scope->CanBeExpression()) return;
    scope_ = scope->AsExpressionParsingScope();
    for (int i = 0; i < kNumberOfErrors; i++) {
      copy(i);
      scope_->clear(i);
    }
  }

  AccumulationScope(const AccumulationScope&) = delete;
  AccumulationScope& operator=(const AccumulationScope&) = delete;

  // Merge errors from the underlying ExpressionParsingScope into this scope.
  // Only keeps the first error across all accumulate calls, and removes the
  // error from the underlying scope.
  void Accumulate() {
    if (scope_ == nullptr) return;
    DCHECK(!scope_->is_verified());
    for (int i = 0; i < kNumberOfErrors; i++) {
      if (!locations_[i].IsValid()) copy(i);
      scope_->clear(i);
    }
  }

  // This is called instead of Accumulate in case the parsed member is already
  // known to be an expression. In that case we don't need to accumulate the
  // expression but rather validate it immediately. We also ignore the pattern
  // error since the parsed member is known to not be a pattern. This is
  // necessary for "{x:1}.y" parsed as part of an assignment pattern. {x:1} will
  // record a pattern error, but "{x:1}.y" is actually a valid as part of an
  // assignment pattern since it's a property access.
  void ValidateExpression() {
    if (scope_ == nullptr) return;
    DCHECK(!scope_->is_verified());
    scope_->ValidateExpression();
    DCHECK(scope_->is_verified());
    scope_->clear(ExpressionParsingScope<Types>::kPatternIndex);
#ifdef DEBUG
    scope_->clear_verified();
#endif
  }

  ~AccumulationScope() {
    if (scope_ == nullptr) return;
    Accumulate();
    for (int i = 0; i < kNumberOfErrors; i++) copy_back(i);
  }

 private:
  void copy(int entry) {
    messages_[entry] = scope_->messages_[entry];
    locations_[entry] = scope_->locations_[entry];
  }

  void copy_back(int entry) {
    if (!locations_[entry].IsValid()) return;
    scope_->messages_[entry] = messages_[entry];
    scope_->locations_[entry] = locations_[entry];
  }

  ExpressionParsingScope<Types>* scope_;
  MessageTemplate messages_[2];
  Scanner::Location locations_[2];
};

// The head of an arrow function is ambiguous between expression, assignment
// pattern and declaration. This keeps track of the additional declaration
// error and allows the scope to be validated as a declaration rather than an
// expression or a pattern.
template <typename Types>
class ArrowHeadParsingScope : public ExpressionParsingScope<Types> {
 public:
  using ParserT = typename Types::Impl;
  using ScopeType = typename ExpressionScope<Types>::ScopeType;

  ArrowHeadParsingScope(ParserT* parser, FunctionKind kind)
      : ExpressionParsingScope<Types>(
            parser,
            kind == FunctionKind::kArrowFunction
                ? ExpressionScope<Types>::kMaybeArrowParameterDeclaration
                : ExpressionScope<
                      Types>::kMaybeAsyncArrowParameterDeclaration) {
    DCHECK(kind == FunctionKind::kAsyncArrowFunction ||
           kind == FunctionKind::kArrowFunction);
    DCHECK(this->CanBeDeclaration());
    DCHECK(!this->IsCertainlyDeclaration());
  }

  ArrowHeadParsingScope(const ArrowHeadParsingScope&) = delete;
  ArrowHeadParsingScope& operator=(const ArrowHeadParsingScope&) = delete;

  void ValidateExpression() {
    // Turns out this is not an arrow head. Clear any possible tracked strict
    // parameter errors, and reinterpret tracked variables as unresolved
    // references.
    this->parser()->next_arrow_function_info_.ClearStrictParameterError();
    ExpressionParsingScope<Types>::ValidateExpression();
    this->parent()->MergeVariableList(this->variable_list());
  }

  DeclarationScope* ValidateAndCreateScope() {
    DCHECK(!this->is_verified());
    DeclarationScope* result = this->parser()->NewFunctionScope(kind());
    if (declaration_error_location.IsValid()) {
      ExpressionScope<Types>::Report(declaration_error_location,
                                     declaration_error_message);
      return result;
    }
    this->ValidatePattern();

    if (!has_simple_parameter_list_) result->SetHasNonSimpleParameters();
    VariableKind kind = PARAMETER_VARIABLE;
    VariableMode mode =
        has_simple_parameter_list_ ? VariableMode::kVar : VariableMode::kLet;
    for (auto& proxy_initializer_pair : *this->variable_list()) {
      VariableProxy* proxy = proxy_initializer_pair.first;
      int initializer_position = proxy_initializer_pair.second;
      // Default values for parameters will have been parsed as assignments so
      // clear the is_assigned bit as they are not actually assignments.
      proxy->clear_is_assigned();
      bool was_added;
      this->parser()->DeclareAndBindVariable(proxy, kind, mode, result,
                                             &was_added, initializer_position);
      if (!was_added) {
        ExpressionScope<Types>::Report(proxy->location(),
                                       MessageTemplate::kParamDupe);
      }
    }

#ifdef DEBUG
    if (!this->has_error()) {
      for (auto declaration : *result->declarations()) {
        DCHECK_NE(declaration->var()->initializer_position(),
                  kNoSourcePosition);
      }
    }
#endif  // DEBUG

    if (uses_this_) result->UsesThis();
    return result;
  }

  void RecordDeclarationError(const Scanner::Location& loc,
                              MessageTemplate message) {
    DCHECK_IMPLIES(!this->has_error(), loc.IsValid());
    declaration_error_location = loc;
    declaration_error_message = message;
  }

  void RecordNonSimpleParameter() { has_simple_parameter_list_ = false; }
  void RecordThisUse() { uses_this_ = true; }

 private:
  FunctionKind kind() const {
    return this->IsAsyncArrowHeadParsingScope()
               ? FunctionKind::kAsyncArrowFunction
               : FunctionKind::kArrowFunction;
  }

  Scanner::Location declaration_error_location = Scanner::Location::invalid();
  MessageTemplate declaration_error_message = MessageTemplate::kNone;
  bool has_simple_parameter_list_ = true;
  bool uses_this_ = false;
};

}  // namespace internal
}  // namespace v8

#endif  // V8_PARSING_EXPRESSION_SCOPE_H_
