}
-VariableProxy::VariableProxy(Zone* zone, Variable* var, int position)
- : Expression(zone, position),
+VariableProxy::VariableProxy(Zone* zone, Variable* var, int start_position,
+ int end_position)
+ : Expression(zone, start_position),
bit_field_(IsThisField::encode(var->is_this()) |
IsAssignedField::encode(false) |
IsResolvedField::encode(false)),
variable_feedback_slot_(FeedbackVectorICSlot::Invalid()),
- raw_name_(var->raw_name()) {
+ raw_name_(var->raw_name()),
+ end_position_(end_position) {
BindTo(var);
}
VariableProxy::VariableProxy(Zone* zone, const AstRawString* name, bool is_this,
- int position)
- : Expression(zone, position),
+ int start_position, int end_position)
+ : Expression(zone, start_position),
bit_field_(IsThisField::encode(is_this) | IsAssignedField::encode(false) |
IsResolvedField::encode(false)),
variable_feedback_slot_(FeedbackVectorICSlot::Invalid()),
- raw_name_(name) {}
+ raw_name_(name),
+ end_position_(end_position) {}
void VariableProxy::BindTo(Variable* var) {
bit_field_ = IsResolvedField::update(bit_field_, true);
}
+ int end_position() const { return end_position_; }
+
// Bind this proxy to the variable var.
void BindTo(Variable* var);
}
protected:
- VariableProxy(Zone* zone, Variable* var, int position);
+ VariableProxy(Zone* zone, Variable* var, int start_position,
+ int end_position);
VariableProxy(Zone* zone, const AstRawString* name, bool is_this,
- int position);
+ int start_position, int end_position);
class IsThisField : public BitField8<bool, 0, 1> {};
class IsAssignedField : public BitField8<bool, 1, 1> {};
const AstRawString* raw_name_; // if !is_resolved_
Variable* var_; // if is_resolved_
};
+ // Position is stored in the AstNode superclass, but VariableProxy needs to
+ // know its end position too (for error messages). It cannot be inferred from
+ // the variable name length because it can contain escapes.
+ int end_position_;
};
}
VariableProxy* NewVariableProxy(Variable* var,
- int pos = RelocInfo::kNoPosition) {
- return new (zone_) VariableProxy(zone_, var, pos);
+ int start_position = RelocInfo::kNoPosition,
+ int end_position = RelocInfo::kNoPosition) {
+ return new (zone_) VariableProxy(zone_, var, start_position, end_position);
}
- VariableProxy* NewVariableProxy(const AstRawString* name,
- bool is_this,
- int position = RelocInfo::kNoPosition) {
- return new (zone_) VariableProxy(zone_, name, is_this, position);
+ VariableProxy* NewVariableProxy(const AstRawString* name, bool is_this,
+ int start_position = RelocInfo::kNoPosition,
+ int end_position = RelocInfo::kNoPosition) {
+ return new (zone_)
+ VariableProxy(zone_, name, is_this, start_position, end_position);
}
Property* NewProperty(Expression* obj, Expression* key, int pos) {
strong_var: ["Please don't use 'var' in strong mode, use 'let' or 'const' instead"],
strong_for_in: ["Please don't use 'for'-'in' loops in strong mode, use 'for'-'of' instead"],
strong_empty: ["Please don't use empty sub-statements in strong mode, make them explicit with '{}' instead"],
+ strong_use_before_declaration: ["Please declare variable '", "%0", "' before use in strong mode"],
sloppy_lexical: ["Block-scoped declarations (let, const, function, class) not yet supported outside strict mode"],
malformed_arrow_function_parameter_list: ["Malformed arrow function parameter list"],
generator_poison_pill: ["'caller' and 'arguments' properties may not be accessed on generator functions."],
Expression* ParserTraits::ExpressionFromIdentifier(const AstRawString* name,
- int pos, Scope* scope,
+ int start_position,
+ int end_position,
+ Scope* scope,
AstNodeFactory* factory) {
if (parser_->fni_ != NULL) parser_->fni_->PushVariableName(name);
// for Traits::DeclareArrowParametersFromExpression() to be able to
// pick the names of the parameters.
return parser_->parsing_lazy_arrow_parameters_
- ? factory->NewVariableProxy(name, false, pos)
- : scope->NewUnresolved(factory, name, pos);
+ ? factory->NewVariableProxy(name, false, start_position,
+ end_position)
+ : scope->NewUnresolved(factory, name, start_position,
+ end_position);
}
// scope.
// Let/const variables in harmony mode are always added to the immediately
// enclosing scope.
- return DeclarationScope(mode)->NewUnresolved(factory(), name, position());
+ return DeclarationScope(mode)->NewUnresolved(factory(), name,
+ scanner()->location().beg_pos,
+ scanner()->location().end_pos);
}
-void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
+Variable* Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
VariableProxy* proxy = declaration->proxy();
DCHECK(proxy->raw_name() != NULL);
const AstRawString* name = proxy->raw_name();
if (var == NULL) {
// Declare the name.
var = declaration_scope->DeclareLocal(
- name, mode, declaration->initialization(), kNotAssigned);
- } else if (IsLexicalVariableMode(mode) || IsLexicalVariableMode(var->mode())
- || ((mode == CONST_LEGACY || var->mode() == CONST_LEGACY) &&
- !declaration_scope->is_script_scope())) {
+ name, mode, declaration->initialization(),
+ declaration->IsFunctionDeclaration() ? Variable::FUNCTION
+ : Variable::NORMAL,
+ kNotAssigned);
+ } else if (IsLexicalVariableMode(mode) ||
+ IsLexicalVariableMode(var->mode()) ||
+ ((mode == CONST_LEGACY || var->mode() == CONST_LEGACY) &&
+ !declaration_scope->is_script_scope())) {
// The name was declared in this scope before; check for conflicting
// re-declarations. We have a conflict if either of the declarations is
// not a var (in script scope, we also have to ignore legacy const for
// ES5 16 for a definition of early errors.
ParserTraits::ReportMessage("var_redeclaration", name);
*ok = false;
- return;
+ return nullptr;
}
Expression* expression = NewThrowTypeError(
"var_redeclaration", name, declaration->position());
if (resolve && var != NULL) {
proxy->BindTo(var);
}
+ return var;
}
Declaration* declaration =
factory()->NewVariableDeclaration(proxy, mode, scope_, pos);
Declare(declaration, true, CHECK_OK);
- proxy->var()->set_initializer_position(pos);
+ proxy->var()->set_initializer_position(position());
Token::Value init_op =
is_strong(language_mode()) ? Token::INIT_CONST : Token::INIT_LET;
VariableProxy* proxy = NewUnresolved(name, mode);
Declaration* declaration =
factory()->NewVariableDeclaration(proxy, mode, scope_, pos);
- Declare(declaration, mode != VAR, CHECK_OK);
+ Variable* var = Declare(declaration, mode != VAR, CHECK_OK);
+ DCHECK_NOT_NULL(var);
+ DCHECK(!proxy->is_resolved() || proxy->var() == var);
nvars++;
if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) {
ReportMessage("too_many_variables");
fni_->RemoveLastFunction();
}
if (decl_props != NULL) *decl_props = kHasInitializers;
- }
-
- // Record the end position of the initializer.
- if (proxy->is_resolved()) {
- proxy->var()->set_initializer_position(position());
+ // End position of the initializer is after the assignment expression.
+ var->set_initializer_position(scanner()->location().end_pos);
+ } else {
+ // End position of the initializer is after the variable.
+ var->set_initializer_position(position());
}
// Make sure that 'const x' and 'let x' initialize 'x' to undefined.
Expect(Token::RPAREN, CHECK_OK);
- catch_variable = catch_scope->DeclareLocal(name, VAR, kCreatedInitialized);
+ catch_variable = catch_scope->DeclareLocal(name, VAR, kCreatedInitialized,
+ Variable::NORMAL);
BlockState block_state(&scope_, catch_scope);
catch_block = ParseBlock(NULL, CHECK_OK);
// make statement: let x = temp_x.
for (int i = 0; i < names->length(); i++) {
VariableProxy* proxy = NewUnresolved(names->at(i), LET);
- Declaration* declaration =
- factory()->NewVariableDeclaration(proxy, LET, scope_, pos);
+ Declaration* declaration = factory()->NewVariableDeclaration(
+ proxy, LET, scope_, RelocInfo::kNoPosition);
Declare(declaration, true, CHECK_OK);
inner_vars.Add(declaration->proxy()->var(), zone());
VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i));
Assignment* assignment = factory()->NewAssignment(
Token::INIT_LET, proxy, temp_proxy, pos);
- Statement* assignment_statement = factory()->NewExpressionStatement(
- assignment, pos);
- proxy->var()->set_initializer_position(pos);
+ Statement* assignment_statement =
+ factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition);
+ proxy->var()->set_initializer_position(init->position());
inner_block->AddStatement(assignment_statement, zone());
}
CHECK_OK);
bool accept_OF = decl_props == kHasNoInitializers;
ForEachStatement::VisitMode mode;
- int each_pos = position();
+ int each_beg_pos = scanner()->location().beg_pos;
+ int each_end_pos = scanner()->location().end_pos;
if (name != NULL && CheckInOrOf(accept_OF, &mode, ok)) {
if (!*ok) return nullptr;
Expression* enumerable = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
- VariableProxy* each = scope_->NewUnresolved(factory(), name, each_pos);
+ VariableProxy* each =
+ scope_->NewUnresolved(factory(), name, each_beg_pos, each_end_pos);
Statement* body = ParseSubStatement(NULL, CHECK_OK);
InitializeForEachStatement(loop, each, enumerable, body);
Block* result =
bool accept_IN = name != NULL && decl_props != kHasInitializers;
bool accept_OF = decl_props == kHasNoInitializers;
ForEachStatement::VisitMode mode;
- int each_pos = position();
+ int each_beg_pos = scanner()->location().beg_pos;
+ int each_end_pos = scanner()->location().end_pos;
if (accept_IN && CheckInOrOf(accept_OF, &mode, ok)) {
if (!*ok) return nullptr;
// implementing stack allocated block scoped variables.
Variable* temp = scope_->DeclarationScope()->NewTemporary(
ast_value_factory()->dot_for_string());
- VariableProxy* temp_proxy = factory()->NewVariableProxy(temp, each_pos);
+ VariableProxy* temp_proxy =
+ factory()->NewVariableProxy(temp, each_beg_pos, each_end_pos);
ForEachStatement* loop =
factory()->NewForEachStatement(mode, labels, stmt_pos);
Target target(&this->target_stack_, loop);
scope_ = for_scope;
Expect(Token::RPAREN, CHECK_OK);
- VariableProxy* each = scope_->NewUnresolved(factory(), name, each_pos);
+ VariableProxy* each =
+ scope_->NewUnresolved(factory(), name, each_beg_pos, each_end_pos);
Statement* body = ParseSubStatement(NULL, CHECK_OK);
Block* body_block =
factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);
bool has_seen_constructor = false;
Expect(Token::LBRACE, CHECK_OK);
+ int body_beg_pos = scanner()->location().beg_pos;
+
const bool has_extends = extends != nullptr;
while (peek() != Token::RBRACE) {
if (Check(Token::SEMICOLON)) continue;
if (name != NULL) {
DCHECK_NOT_NULL(proxy);
DCHECK_NOT_NULL(block_scope);
- proxy->var()->set_initializer_position(end_pos);
+ proxy->var()->set_initializer_position(body_beg_pos);
}
return factory()->NewClassLiteral(name, block_scope, proxy, extends,
int end_pos);
Literal* ExpressionFromLiteral(Token::Value token, int pos, Scanner* scanner,
AstNodeFactory* factory);
- Expression* ExpressionFromIdentifier(const AstRawString* name, int pos,
+ Expression* ExpressionFromIdentifier(const AstRawString* name,
+ int start_position, int end_position,
Scope* scope, AstNodeFactory* factory);
Expression* ExpressionFromString(int pos, Scanner* scanner,
AstNodeFactory* factory);
// Parser support
VariableProxy* NewUnresolved(const AstRawString* name, VariableMode mode);
- void Declare(Declaration* declaration, bool resolve, bool* ok);
+ Variable* Declare(Declaration* declaration, bool resolve, bool* ok);
bool TargetStackContainsLabel(const AstRawString* label);
BreakableStatement* LookupBreakTarget(const AstRawString* label, bool* ok);
}
static PreParserExpression ExpressionFromIdentifier(
- PreParserIdentifier name, int pos, Scope* scope,
- PreParserFactory* factory) {
+ PreParserIdentifier name, int start_position, int end_position,
+ Scope* scope, PreParserFactory* factory) {
return PreParserExpression::FromIdentifier(name);
}
// '(' Expression ')'
// TemplateLiteral
- int pos = peek_position();
+ int beg_pos = scanner()->peek_location().beg_pos;
+ int end_pos = scanner()->peek_location().end_pos;
ExpressionT result = this->EmptyExpression();
Token::Value token = peek();
switch (token) {
case Token::THIS: {
Consume(Token::THIS);
scope_->RecordThisUsage();
- result = this->ThisExpression(scope_, factory(), pos);
+ result = this->ThisExpression(scope_, factory(), beg_pos);
break;
}
case Token::FALSE_LITERAL:
case Token::NUMBER:
Next();
- result = this->ExpressionFromLiteral(token, pos, scanner(), factory());
+ result =
+ this->ExpressionFromLiteral(token, beg_pos, scanner(), factory());
break;
case Token::IDENTIFIER:
case Token::FUTURE_STRICT_RESERVED_WORD: {
// Using eval or arguments in this context is OK even in strict mode.
IdentifierT name = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
- result = this->ExpressionFromIdentifier(name, pos, scope_, factory());
+ result = this->ExpressionFromIdentifier(name, beg_pos, end_pos, scope_,
+ factory());
break;
}
case Token::STRING: {
Consume(Token::STRING);
- result = this->ExpressionFromString(pos, scanner(), factory());
+ result = this->ExpressionFromString(beg_pos, scanner(), factory());
break;
}
// for which an empty parameter list "()" is valid input.
Consume(Token::RPAREN);
result = this->ParseArrowFunctionLiteral(
- pos, this->EmptyArrowParamList(), CHECK_OK);
+ beg_pos, this->EmptyArrowParamList(), CHECK_OK);
} else {
// Heuristically try to detect immediately called functions before
// seeing the call parentheses.
case Token::TEMPLATE_SPAN:
case Token::TEMPLATE_TAIL:
- result =
- this->ParseTemplateLiteral(Traits::NoTemplateTag(), pos, CHECK_OK);
+ result = this->ParseTemplateLiteral(Traits::NoTemplateTag(), beg_pos,
+ CHECK_OK);
break;
case Token::MOD:
bool is_generator = allow_harmony_object_literals_ && Check(Token::MUL);
Token::Value name_token = peek();
- int next_pos = peek_position();
+ int next_beg_pos = scanner()->peek_location().beg_pos;
+ int next_end_pos = scanner()->peek_location().end_pos;
ExpressionT name_expression = ParsePropertyName(
&name, &is_get, &is_set, &name_is_static, is_computed_name,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
this->is_generator())) {
DCHECK(!*is_computed_name);
DCHECK(!is_static);
- value = this->ExpressionFromIdentifier(name, next_pos, scope_, factory());
+ value = this->ExpressionFromIdentifier(name, next_beg_pos, next_end_pos,
+ scope_, factory());
return factory()->NewObjectLiteralProperty(
name_expression, value, ObjectLiteralProperty::COMPUTED, false, false);
// Allocate the variables.
{
AstNodeFactory ast_node_factory(info->ast_value_factory());
- if (!top->AllocateVariables(info, &ast_node_factory)) return false;
+ if (!top->AllocateVariables(info, &ast_node_factory)) {
+ DCHECK(top->pending_error_handler_.has_pending_error());
+ top->pending_error_handler_.ThrowPendingError(info->isolate(),
+ info->script());
+ return false;
+ }
}
#ifdef DEBUG
Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode,
- InitializationFlag init_flag,
+ InitializationFlag init_flag, Variable::Kind kind,
MaybeAssignedFlag maybe_assigned_flag) {
DCHECK(!already_resolved());
// This function handles VAR, LET, and CONST modes. DYNAMIC variables are
// explicitly, and TEMPORARY variables are allocated via NewTemporary().
DCHECK(IsDeclaredVariableMode(mode));
++num_var_or_const_;
- return variables_.Declare(this, name, mode, true, Variable::NORMAL, init_flag,
+ return variables_.Declare(this, name, mode, true, kind, init_flag,
maybe_assigned_flag);
}
}
+void Scope::ReportMessage(int start_position, int end_position,
+ const char* message, const AstRawString* arg) {
+ // Propagate the error to the topmost scope targeted by this scope analysis
+ // phase.
+ Scope* top = this;
+ while (!top->is_script_scope() && !top->outer_scope()->already_resolved()) {
+ top = top->outer_scope();
+ }
+
+ top->pending_error_handler_.ReportMessageAt(start_position, end_position,
+ message, arg, kReferenceError);
+}
+
+
#ifdef DEBUG
static const char* Header(ScopeType scope_type) {
switch (scope_type) {
switch (binding_kind) {
case BOUND:
// We found a variable binding.
+ if (is_strong(language_mode())) {
+ // Check for declaration-after use (for variables) in strong mode. Note
+ // that we can only do this in the case where we have seen the
+ // declaration. And we always allow referencing functions (for now).
+
+ // If both the use and the declaration are inside an eval scope
+ // (possibly indirectly), or one of them is, we need to check whether
+ // they are inside the same eval scope or different
+ // ones.
+
+ // TODO(marja,rossberg): Detect errors across different evals (depends
+ // on the future of eval in strong mode).
+ const Scope* eval_for_use = NearestOuterEvalScope();
+ const Scope* eval_for_declaration =
+ var->scope()->NearestOuterEvalScope();
+
+ if (proxy->position() != RelocInfo::kNoPosition &&
+ proxy->position() < var->initializer_position() &&
+ !var->is_function() && eval_for_use == eval_for_declaration) {
+ DCHECK(proxy->end_position() != RelocInfo::kNoPosition);
+ ReportMessage(proxy->position(), proxy->end_position(),
+ "strong_use_before_declaration", proxy->raw_name());
+ return false;
+ }
+ }
break;
case BOUND_EVAL_SHADOWED:
#define V8_SCOPES_H_
#include "src/ast.h"
+#include "src/pending-compilation-error-handler.h"
#include "src/zone.h"
namespace v8 {
// Declare a local variable in this scope. If the variable has been
// declared before, the previously declared variable is returned.
Variable* DeclareLocal(const AstRawString* name, VariableMode mode,
- InitializationFlag init_flag,
+ InitializationFlag init_flag, Variable::Kind kind,
MaybeAssignedFlag maybe_assigned_flag = kNotAssigned);
// Declare an implicit global variable in this scope which must be a
// Create a new unresolved variable.
VariableProxy* NewUnresolved(AstNodeFactory* factory,
const AstRawString* name,
- int position = RelocInfo::kNoPosition) {
+ int start_position = RelocInfo::kNoPosition,
+ int end_position = RelocInfo::kNoPosition) {
// Note that we must not share the unresolved variables with
// the same name because they may be removed selectively via
// RemoveUnresolved().
DCHECK(!already_resolved());
- VariableProxy* proxy = factory->NewVariableProxy(name, false, position);
+ VariableProxy* proxy =
+ factory->NewVariableProxy(name, false, start_position, end_position);
unresolved_.Add(proxy, zone_);
return proxy;
}
// Does any inner scope access "this".
bool inner_uses_this() const { return inner_scope_uses_this_; }
+ const Scope* NearestOuterEvalScope() const {
+ if (is_eval_scope()) return this;
+ if (outer_scope() == nullptr) return nullptr;
+ return outer_scope()->NearestOuterEvalScope();
+ }
+
// ---------------------------------------------------------------------------
// Accessors.
return params_.Contains(variables_.Lookup(name));
}
+ // Error handling.
+ void ReportMessage(int start_position, int end_position, const char* message,
+ const AstRawString* arg);
+
// ---------------------------------------------------------------------------
// Debugging.
AstValueFactory* ast_value_factory_;
Zone* zone_;
+
+ PendingCompilationErrorHandler pending_error_handler_;
};
} } // namespace v8::internal
class Variable: public ZoneObject {
public:
- enum Kind { NORMAL, THIS, NEW_TARGET, ARGUMENTS };
+ enum Kind { NORMAL, FUNCTION, THIS, NEW_TARGET, ARGUMENTS };
enum Location {
// Before and during variable allocation, a variable whose location is
return initialization_flag_ == kNeedsInitialization;
}
+ bool is_function() const { return kind_ == FUNCTION; }
bool is_this() const { return kind_ == THIS; }
bool is_new_target() const { return kind_ == NEW_TARGET; }
bool is_arguments() const { return kind_ == ARGUMENTS; }
--- /dev/null
+// Copyright 2015 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.
+
+// Flags: --strong-mode --harmony_rest_parameters --harmony_arrow_functions --harmony_classes --harmony-computed-property-names
+
+// Note that it's essential for these tests that the reference is inside dead
+// code (because we already produce ReferenceErrors for run-time unresolved
+// variables and don't want to confuse those with strong mode errors). But the
+// errors should *not* be inside lazy, unexecuted functions, since lazy parsing
+// doesn't produce strong mode scoping errors).
+
+// In addition, assertThrows will call eval and that changes variable binding
+// types (see e.g., UNBOUND_EVAL_SHADOWED). We can avoid unwanted side effects
+// by wrapping the code to be tested inside an outer function.
+function assertThrowsHelper(code, error) {
+ "use strict";
+ let prologue = "(function outer() { ";
+ let epilogue = " })();";
+ assertThrows(prologue + code + epilogue, error);
+}
+
+(function DeclarationAfterUse() {
+ // Note that these tests only test cases where the declaration is found but is
+ // after the use. In particular, we cannot yet detect cases where the use can
+ // possibly bind to a global variable.
+ assertThrowsHelper("'use strong'; if (false) { x; let x = 0; }",
+ ReferenceError);
+ assertThrowsHelper(
+ "function f() { 'use strong'; if (false) { x; let x = 0; } } f();",
+ ReferenceError);
+ assertThrowsHelper(
+ "'use strong'; function f() { if (false) { x; } } let x = 0; f();",
+ ReferenceError);
+
+ assertThrowsHelper(
+ "function f() { 'use strong'; if (false) { x; } } var x = 0; f();",
+ ReferenceError);
+ assertThrowsHelper(
+ "function f() { 'use strong'; if (false) { x; } } var x; f();",
+ ReferenceError);
+ // Errors are also detected when the declaration and the use are in the same
+ // eval scope.
+ assertThrowsHelper("'use strong'; eval('x; let x = 0;')", ReferenceError);
+
+ // Use occurring in the initializer of the declaration:
+ assertThrowsHelper("'use strong'; if (false) { let x = x + 1; }",
+ ReferenceError);
+ assertThrowsHelper("'use strong'; if (false) { let x = x; }",
+ ReferenceError);
+ assertThrowsHelper("'use strong'; if (false) { let x = y, y = 4; }",
+ ReferenceError);
+ assertThrowsHelper("'use strong'; if (false) { let x = function() { x; } }",
+ ReferenceError);
+ assertThrowsHelper("'use strong'; if (false) { let x = a => { x; } }",
+ ReferenceError);
+ assertThrowsHelper(
+ "'use strong'; if (false) { function f() {}; let x = f(x); }",
+ ReferenceError);
+ assertThrowsHelper("'use strong'; if (false) { const x = x; }",
+ ReferenceError);
+ assertThrowsHelper("'use strong'; if (false) { const x = function() { x; } }",
+ ReferenceError);
+ assertThrowsHelper("'use strong'; if (false) { const x = a => { x; } }",
+ ReferenceError);
+ assertThrowsHelper(
+ "'use strong'; if (false) { function f() {}; const x = f(x); }",
+ ReferenceError);
+
+ assertThrowsHelper(
+ "'use strong'; if (false) { for (let x = x; ; ) { } }",
+ ReferenceError);
+ assertThrowsHelper(
+ "'use strong'; if (false) { for (const x = x; ; ) { } }",
+ ReferenceError);
+ assertThrowsHelper(
+ "'use strong'; if (false) { for (let x = y, y; ; ) { } }",
+ ReferenceError);
+ assertThrowsHelper(
+ "'use strong'; if (false) { for (const x = y, y = 0; ; ) { } }",
+ ReferenceError);
+
+ // Computed property names
+ assertThrowsHelper(
+ "'use strong'; if (false) { let o = { 'a': 'b', [o.a]: 'c'}; }",
+ ReferenceError);
+})();
+
+
+(function DeclarationAfterUseInClasses() {
+ assertThrowsHelper("'use strong'; if (false) { class C extends C { } }",
+ ReferenceError);
+ assertThrowsHelper(
+ "'use strong'; if (false) { let C = class C2 extends C { } }",
+ ReferenceError);
+ assertThrowsHelper(
+ "'use strong'; if (false) { let C = class C2 extends C2 { } }",
+ ReferenceError);
+
+ assertThrowsHelper(
+ "'use strong'; if (false) { let C = class C2 { constructor() { C; } } }",
+ ReferenceError);
+
+ assertThrowsHelper(
+ "'use strong'; if (false) { let C = class C2 { method() { C; } } }",
+ ReferenceError);
+
+ assertThrowsHelper(
+ "'use strong'; if (false) { let C = class C2 { " +
+ "static a() { return 'A'; } [C.a()]() { return 'B'; } }; }",
+ ReferenceError);
+
+ // TODO(marja, rossberg): More tests related to computed property names in
+ // classes + recognize more errors. This one is not recognized as an error
+ // yet:
+ // let C = class C2 {
+ // static a() { return 'A'; }
+ // [C2.a()]() { return 'B'; } << C2 should not be allowed here
+ // };
+})();
+
+
+(function UsesWhichAreFine() {
+ "use strong";
+
+ let var1 = 0;
+ var1;
+
+ let var2a = 0, var2b = var2a + 1, var2c = 2 + var2b;
+
+ for (let var3 = 0; var3 < 1; var3++) {
+ var3;
+ }
+
+ for (let var4a = 0, var4b = var4a; var4a + var4b < 4; var4a++, var4b++) {
+ var4a;
+ var4b;
+ }
+
+ let var5 = 5;
+ for (; var5 < 10; ++var5) { }
+
+ let arr = [1, 2];
+ for (let i of arr) {
+ i;
+ }
+
+ let var6 = [1, 2];
+ // The second var6 resolves to outside (not to the first var6).
+ for (let var6 of var6) { var6; }
+
+ try {
+ throw "error";
+ } catch (e) {
+ e;
+ }
+
+ function func1() { func1; this; }
+ func1();
+ func1;
+
+ function * func2() { func2; this; }
+ func2();
+ func2;
+
+ function func4(p, ...rest) { p; rest; this; func2; }
+ func4();
+
+ let func5 = (p1, p2) => { p1; p2; };
+ func5();
+
+ function func6() {
+ var1, var2a, var2b, var2c;
+ }
+
+ (function eval1() {
+ let var7 = 0; // Declaration position will be something large.
+ // But use position will be something small, however, this is not an error,
+ // since the use is inside an eval scope.
+ eval("var7;");
+ })();
+
+ class C1 { constructor() { C1; } }; new C1();
+ let C2 = class C3 { constructor() { C3; } }; new C2();
+
+ class C4 { method() { C4; method; } }; new C4();
+ let C5 = class C6 { method() { C6; method; } }; new C5();
+})();