[strong] Declaration-after-use errors.
We cannot yet detect use-before-declaration in general, because for that we'd need to analyze the context when compiling. But we can detect an error case where we first see a use, then a declaration. For this, I also added end position tracking (needed for error messages) to VariableProxy. Note: the position naming is completely inconsistent: start_position & end_position, position & end_position, pos & end_pos, beg_pos & end_pos, to name a few. This doesn't fix all of it, but tries to unify towards start_position & end_position whenever possible w/ minimal changes. BUG= Review URL: https://codereview.chromium.org/943543002 Cr-Commit-Position: refs/heads/master@{#26880}
This commit is contained in:
parent
77d3ae0e11
commit
1eddcf5b71
15
src/ast.cc
15
src/ast.cc
@ -59,24 +59,27 @@ bool Expression::IsUndefinedLiteral(Isolate* isolate) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VariableProxy::VariableProxy(Zone* zone, Variable* var, int position)
|
VariableProxy::VariableProxy(Zone* zone, Variable* var, int start_position,
|
||||||
: Expression(zone, position),
|
int end_position)
|
||||||
|
: Expression(zone, start_position),
|
||||||
bit_field_(IsThisField::encode(var->is_this()) |
|
bit_field_(IsThisField::encode(var->is_this()) |
|
||||||
IsAssignedField::encode(false) |
|
IsAssignedField::encode(false) |
|
||||||
IsResolvedField::encode(false)),
|
IsResolvedField::encode(false)),
|
||||||
variable_feedback_slot_(FeedbackVectorICSlot::Invalid()),
|
variable_feedback_slot_(FeedbackVectorICSlot::Invalid()),
|
||||||
raw_name_(var->raw_name()) {
|
raw_name_(var->raw_name()),
|
||||||
|
end_position_(end_position) {
|
||||||
BindTo(var);
|
BindTo(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
VariableProxy::VariableProxy(Zone* zone, const AstRawString* name, bool is_this,
|
VariableProxy::VariableProxy(Zone* zone, const AstRawString* name, bool is_this,
|
||||||
int position)
|
int start_position, int end_position)
|
||||||
: Expression(zone, position),
|
: Expression(zone, start_position),
|
||||||
bit_field_(IsThisField::encode(is_this) | IsAssignedField::encode(false) |
|
bit_field_(IsThisField::encode(is_this) | IsAssignedField::encode(false) |
|
||||||
IsResolvedField::encode(false)),
|
IsResolvedField::encode(false)),
|
||||||
variable_feedback_slot_(FeedbackVectorICSlot::Invalid()),
|
variable_feedback_slot_(FeedbackVectorICSlot::Invalid()),
|
||||||
raw_name_(name) {}
|
raw_name_(name),
|
||||||
|
end_position_(end_position) {}
|
||||||
|
|
||||||
|
|
||||||
void VariableProxy::BindTo(Variable* var) {
|
void VariableProxy::BindTo(Variable* var) {
|
||||||
|
25
src/ast.h
25
src/ast.h
@ -1631,6 +1631,8 @@ class VariableProxy FINAL : public Expression {
|
|||||||
bit_field_ = IsResolvedField::update(bit_field_, true);
|
bit_field_ = IsResolvedField::update(bit_field_, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int end_position() const { return end_position_; }
|
||||||
|
|
||||||
// Bind this proxy to the variable var.
|
// Bind this proxy to the variable var.
|
||||||
void BindTo(Variable* var);
|
void BindTo(Variable* var);
|
||||||
|
|
||||||
@ -1653,10 +1655,11 @@ class VariableProxy FINAL : public Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
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,
|
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 IsThisField : public BitField8<bool, 0, 1> {};
|
||||||
class IsAssignedField : public BitField8<bool, 1, 1> {};
|
class IsAssignedField : public BitField8<bool, 1, 1> {};
|
||||||
@ -1670,6 +1673,10 @@ class VariableProxy FINAL : public Expression {
|
|||||||
const AstRawString* raw_name_; // if !is_resolved_
|
const AstRawString* raw_name_; // if !is_resolved_
|
||||||
Variable* var_; // 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_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -3353,14 +3360,16 @@ class AstNodeFactory FINAL BASE_EMBEDDED {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VariableProxy* NewVariableProxy(Variable* var,
|
VariableProxy* NewVariableProxy(Variable* var,
|
||||||
int pos = RelocInfo::kNoPosition) {
|
int start_position = RelocInfo::kNoPosition,
|
||||||
return new (zone_) VariableProxy(zone_, var, pos);
|
int end_position = RelocInfo::kNoPosition) {
|
||||||
|
return new (zone_) VariableProxy(zone_, var, start_position, end_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
VariableProxy* NewVariableProxy(const AstRawString* name,
|
VariableProxy* NewVariableProxy(const AstRawString* name, bool is_this,
|
||||||
bool is_this,
|
int start_position = RelocInfo::kNoPosition,
|
||||||
int position = RelocInfo::kNoPosition) {
|
int end_position = RelocInfo::kNoPosition) {
|
||||||
return new (zone_) VariableProxy(zone_, name, is_this, position);
|
return new (zone_)
|
||||||
|
VariableProxy(zone_, name, is_this, start_position, end_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
Property* NewProperty(Expression* obj, Expression* key, int pos) {
|
Property* NewProperty(Expression* obj, Expression* key, int pos) {
|
||||||
|
@ -168,6 +168,7 @@ var kMessages = {
|
|||||||
strong_var: ["Please don't use 'var' in strong mode, use 'let' or 'const' instead"],
|
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_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_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"],
|
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"],
|
malformed_arrow_function_parameter_list: ["Malformed arrow function parameter list"],
|
||||||
generator_poison_pill: ["'caller' and 'arguments' properties may not be accessed on generator functions."],
|
generator_poison_pill: ["'caller' and 'arguments' properties may not be accessed on generator functions."],
|
||||||
|
@ -706,7 +706,9 @@ Literal* ParserTraits::ExpressionFromLiteral(Token::Value token, int pos,
|
|||||||
|
|
||||||
|
|
||||||
Expression* ParserTraits::ExpressionFromIdentifier(const AstRawString* name,
|
Expression* ParserTraits::ExpressionFromIdentifier(const AstRawString* name,
|
||||||
int pos, Scope* scope,
|
int start_position,
|
||||||
|
int end_position,
|
||||||
|
Scope* scope,
|
||||||
AstNodeFactory* factory) {
|
AstNodeFactory* factory) {
|
||||||
if (parser_->fni_ != NULL) parser_->fni_->PushVariableName(name);
|
if (parser_->fni_ != NULL) parser_->fni_->PushVariableName(name);
|
||||||
|
|
||||||
@ -715,8 +717,10 @@ Expression* ParserTraits::ExpressionFromIdentifier(const AstRawString* name,
|
|||||||
// for Traits::DeclareArrowParametersFromExpression() to be able to
|
// for Traits::DeclareArrowParametersFromExpression() to be able to
|
||||||
// pick the names of the parameters.
|
// pick the names of the parameters.
|
||||||
return parser_->parsing_lazy_arrow_parameters_
|
return parser_->parsing_lazy_arrow_parameters_
|
||||||
? factory->NewVariableProxy(name, false, pos)
|
? factory->NewVariableProxy(name, false, start_position,
|
||||||
: scope->NewUnresolved(factory, name, pos);
|
end_position)
|
||||||
|
: scope->NewUnresolved(factory, name, start_position,
|
||||||
|
end_position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1776,11 +1780,13 @@ VariableProxy* Parser::NewUnresolved(const AstRawString* name,
|
|||||||
// scope.
|
// scope.
|
||||||
// Let/const variables in harmony mode are always added to the immediately
|
// Let/const variables in harmony mode are always added to the immediately
|
||||||
// enclosing scope.
|
// 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();
|
VariableProxy* proxy = declaration->proxy();
|
||||||
DCHECK(proxy->raw_name() != NULL);
|
DCHECK(proxy->raw_name() != NULL);
|
||||||
const AstRawString* name = proxy->raw_name();
|
const AstRawString* name = proxy->raw_name();
|
||||||
@ -1806,9 +1812,13 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
|
|||||||
if (var == NULL) {
|
if (var == NULL) {
|
||||||
// Declare the name.
|
// Declare the name.
|
||||||
var = declaration_scope->DeclareLocal(
|
var = declaration_scope->DeclareLocal(
|
||||||
name, mode, declaration->initialization(), kNotAssigned);
|
name, mode, declaration->initialization(),
|
||||||
} else if (IsLexicalVariableMode(mode) || IsLexicalVariableMode(var->mode())
|
declaration->IsFunctionDeclaration() ? Variable::FUNCTION
|
||||||
|| ((mode == CONST_LEGACY || var->mode() == CONST_LEGACY) &&
|
: Variable::NORMAL,
|
||||||
|
kNotAssigned);
|
||||||
|
} else if (IsLexicalVariableMode(mode) ||
|
||||||
|
IsLexicalVariableMode(var->mode()) ||
|
||||||
|
((mode == CONST_LEGACY || var->mode() == CONST_LEGACY) &&
|
||||||
!declaration_scope->is_script_scope())) {
|
!declaration_scope->is_script_scope())) {
|
||||||
// The name was declared in this scope before; check for conflicting
|
// The name was declared in this scope before; check for conflicting
|
||||||
// re-declarations. We have a conflict if either of the declarations is
|
// re-declarations. We have a conflict if either of the declarations is
|
||||||
@ -1829,7 +1839,7 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
|
|||||||
// ES5 16 for a definition of early errors.
|
// ES5 16 for a definition of early errors.
|
||||||
ParserTraits::ReportMessage("var_redeclaration", name);
|
ParserTraits::ReportMessage("var_redeclaration", name);
|
||||||
*ok = false;
|
*ok = false;
|
||||||
return;
|
return nullptr;
|
||||||
}
|
}
|
||||||
Expression* expression = NewThrowTypeError(
|
Expression* expression = NewThrowTypeError(
|
||||||
"var_redeclaration", name, declaration->position());
|
"var_redeclaration", name, declaration->position());
|
||||||
@ -1903,6 +1913,7 @@ void Parser::Declare(Declaration* declaration, bool resolve, bool* ok) {
|
|||||||
if (resolve && var != NULL) {
|
if (resolve && var != NULL) {
|
||||||
proxy->BindTo(var);
|
proxy->BindTo(var);
|
||||||
}
|
}
|
||||||
|
return var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2023,7 +2034,7 @@ Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names,
|
|||||||
Declaration* declaration =
|
Declaration* declaration =
|
||||||
factory()->NewVariableDeclaration(proxy, mode, scope_, pos);
|
factory()->NewVariableDeclaration(proxy, mode, scope_, pos);
|
||||||
Declare(declaration, true, CHECK_OK);
|
Declare(declaration, true, CHECK_OK);
|
||||||
proxy->var()->set_initializer_position(pos);
|
proxy->var()->set_initializer_position(position());
|
||||||
|
|
||||||
Token::Value init_op =
|
Token::Value init_op =
|
||||||
is_strong(language_mode()) ? Token::INIT_CONST : Token::INIT_LET;
|
is_strong(language_mode()) ? Token::INIT_CONST : Token::INIT_LET;
|
||||||
@ -2232,7 +2243,9 @@ Block* Parser::ParseVariableDeclarations(
|
|||||||
VariableProxy* proxy = NewUnresolved(name, mode);
|
VariableProxy* proxy = NewUnresolved(name, mode);
|
||||||
Declaration* declaration =
|
Declaration* declaration =
|
||||||
factory()->NewVariableDeclaration(proxy, mode, scope_, pos);
|
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++;
|
nvars++;
|
||||||
if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) {
|
if (declaration_scope->num_var_or_const() > kMaxNumFunctionLocals) {
|
||||||
ReportMessage("too_many_variables");
|
ReportMessage("too_many_variables");
|
||||||
@ -2286,11 +2299,11 @@ Block* Parser::ParseVariableDeclarations(
|
|||||||
fni_->RemoveLastFunction();
|
fni_->RemoveLastFunction();
|
||||||
}
|
}
|
||||||
if (decl_props != NULL) *decl_props = kHasInitializers;
|
if (decl_props != NULL) *decl_props = kHasInitializers;
|
||||||
}
|
// End position of the initializer is after the assignment expression.
|
||||||
|
var->set_initializer_position(scanner()->location().end_pos);
|
||||||
// Record the end position of the initializer.
|
} else {
|
||||||
if (proxy->is_resolved()) {
|
// End position of the initializer is after the variable.
|
||||||
proxy->var()->set_initializer_position(position());
|
var->set_initializer_position(position());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that 'const x' and 'let x' initialize 'x' to undefined.
|
// Make sure that 'const x' and 'let x' initialize 'x' to undefined.
|
||||||
@ -2795,7 +2808,8 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
|
|||||||
|
|
||||||
Expect(Token::RPAREN, CHECK_OK);
|
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);
|
BlockState block_state(&scope_, catch_scope);
|
||||||
catch_block = ParseBlock(NULL, CHECK_OK);
|
catch_block = ParseBlock(NULL, CHECK_OK);
|
||||||
|
|
||||||
@ -3087,16 +3101,16 @@ Statement* Parser::DesugarLetBindingsInForStatement(
|
|||||||
// make statement: let x = temp_x.
|
// make statement: let x = temp_x.
|
||||||
for (int i = 0; i < names->length(); i++) {
|
for (int i = 0; i < names->length(); i++) {
|
||||||
VariableProxy* proxy = NewUnresolved(names->at(i), LET);
|
VariableProxy* proxy = NewUnresolved(names->at(i), LET);
|
||||||
Declaration* declaration =
|
Declaration* declaration = factory()->NewVariableDeclaration(
|
||||||
factory()->NewVariableDeclaration(proxy, LET, scope_, pos);
|
proxy, LET, scope_, RelocInfo::kNoPosition);
|
||||||
Declare(declaration, true, CHECK_OK);
|
Declare(declaration, true, CHECK_OK);
|
||||||
inner_vars.Add(declaration->proxy()->var(), zone());
|
inner_vars.Add(declaration->proxy()->var(), zone());
|
||||||
VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i));
|
VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i));
|
||||||
Assignment* assignment = factory()->NewAssignment(
|
Assignment* assignment = factory()->NewAssignment(
|
||||||
Token::INIT_LET, proxy, temp_proxy, pos);
|
Token::INIT_LET, proxy, temp_proxy, pos);
|
||||||
Statement* assignment_statement = factory()->NewExpressionStatement(
|
Statement* assignment_statement =
|
||||||
assignment, pos);
|
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition);
|
||||||
proxy->var()->set_initializer_position(pos);
|
proxy->var()->set_initializer_position(init->position());
|
||||||
inner_block->AddStatement(assignment_statement, zone());
|
inner_block->AddStatement(assignment_statement, zone());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3244,7 +3258,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
|||||||
CHECK_OK);
|
CHECK_OK);
|
||||||
bool accept_OF = decl_props == kHasNoInitializers;
|
bool accept_OF = decl_props == kHasNoInitializers;
|
||||||
ForEachStatement::VisitMode mode;
|
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 (name != NULL && CheckInOrOf(accept_OF, &mode, ok)) {
|
||||||
if (!*ok) return nullptr;
|
if (!*ok) return nullptr;
|
||||||
@ -3255,7 +3270,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
|||||||
Expression* enumerable = ParseExpression(true, CHECK_OK);
|
Expression* enumerable = ParseExpression(true, CHECK_OK);
|
||||||
Expect(Token::RPAREN, 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);
|
Statement* body = ParseSubStatement(NULL, CHECK_OK);
|
||||||
InitializeForEachStatement(loop, each, enumerable, body);
|
InitializeForEachStatement(loop, each, enumerable, body);
|
||||||
Block* result =
|
Block* result =
|
||||||
@ -3282,7 +3298,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
|||||||
bool accept_IN = name != NULL && decl_props != kHasInitializers;
|
bool accept_IN = name != NULL && decl_props != kHasInitializers;
|
||||||
bool accept_OF = decl_props == kHasNoInitializers;
|
bool accept_OF = decl_props == kHasNoInitializers;
|
||||||
ForEachStatement::VisitMode mode;
|
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 (accept_IN && CheckInOrOf(accept_OF, &mode, ok)) {
|
||||||
if (!*ok) return nullptr;
|
if (!*ok) return nullptr;
|
||||||
@ -3304,7 +3321,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
|||||||
// implementing stack allocated block scoped variables.
|
// implementing stack allocated block scoped variables.
|
||||||
Variable* temp = scope_->DeclarationScope()->NewTemporary(
|
Variable* temp = scope_->DeclarationScope()->NewTemporary(
|
||||||
ast_value_factory()->dot_for_string());
|
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 =
|
ForEachStatement* loop =
|
||||||
factory()->NewForEachStatement(mode, labels, stmt_pos);
|
factory()->NewForEachStatement(mode, labels, stmt_pos);
|
||||||
Target target(&this->target_stack_, loop);
|
Target target(&this->target_stack_, loop);
|
||||||
@ -3315,7 +3333,8 @@ Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
|||||||
scope_ = for_scope;
|
scope_ = for_scope;
|
||||||
Expect(Token::RPAREN, 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);
|
Statement* body = ParseSubStatement(NULL, CHECK_OK);
|
||||||
Block* body_block =
|
Block* body_block =
|
||||||
factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);
|
factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);
|
||||||
@ -4099,6 +4118,8 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name,
|
|||||||
bool has_seen_constructor = false;
|
bool has_seen_constructor = false;
|
||||||
|
|
||||||
Expect(Token::LBRACE, CHECK_OK);
|
Expect(Token::LBRACE, CHECK_OK);
|
||||||
|
int body_beg_pos = scanner()->location().beg_pos;
|
||||||
|
|
||||||
const bool has_extends = extends != nullptr;
|
const bool has_extends = extends != nullptr;
|
||||||
while (peek() != Token::RBRACE) {
|
while (peek() != Token::RBRACE) {
|
||||||
if (Check(Token::SEMICOLON)) continue;
|
if (Check(Token::SEMICOLON)) continue;
|
||||||
@ -4138,7 +4159,7 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name,
|
|||||||
if (name != NULL) {
|
if (name != NULL) {
|
||||||
DCHECK_NOT_NULL(proxy);
|
DCHECK_NOT_NULL(proxy);
|
||||||
DCHECK_NOT_NULL(block_scope);
|
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,
|
return factory()->NewClassLiteral(name, block_scope, proxy, extends,
|
||||||
|
@ -542,7 +542,8 @@ class ParserTraits {
|
|||||||
int end_pos);
|
int end_pos);
|
||||||
Literal* ExpressionFromLiteral(Token::Value token, int pos, Scanner* scanner,
|
Literal* ExpressionFromLiteral(Token::Value token, int pos, Scanner* scanner,
|
||||||
AstNodeFactory* factory);
|
AstNodeFactory* factory);
|
||||||
Expression* ExpressionFromIdentifier(const AstRawString* name, int pos,
|
Expression* ExpressionFromIdentifier(const AstRawString* name,
|
||||||
|
int start_position, int end_position,
|
||||||
Scope* scope, AstNodeFactory* factory);
|
Scope* scope, AstNodeFactory* factory);
|
||||||
Expression* ExpressionFromString(int pos, Scanner* scanner,
|
Expression* ExpressionFromString(int pos, Scanner* scanner,
|
||||||
AstNodeFactory* factory);
|
AstNodeFactory* factory);
|
||||||
@ -799,7 +800,7 @@ class Parser : public ParserBase<ParserTraits> {
|
|||||||
|
|
||||||
// Parser support
|
// Parser support
|
||||||
VariableProxy* NewUnresolved(const AstRawString* name, VariableMode mode);
|
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);
|
bool TargetStackContainsLabel(const AstRawString* label);
|
||||||
BreakableStatement* LookupBreakTarget(const AstRawString* label, bool* ok);
|
BreakableStatement* LookupBreakTarget(const AstRawString* label, bool* ok);
|
||||||
|
@ -1408,8 +1408,8 @@ class PreParserTraits {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static PreParserExpression ExpressionFromIdentifier(
|
static PreParserExpression ExpressionFromIdentifier(
|
||||||
PreParserIdentifier name, int pos, Scope* scope,
|
PreParserIdentifier name, int start_position, int end_position,
|
||||||
PreParserFactory* factory) {
|
Scope* scope, PreParserFactory* factory) {
|
||||||
return PreParserExpression::FromIdentifier(name);
|
return PreParserExpression::FromIdentifier(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1859,14 +1859,15 @@ ParserBase<Traits>::ParsePrimaryExpression(bool* ok) {
|
|||||||
// '(' Expression ')'
|
// '(' Expression ')'
|
||||||
// TemplateLiteral
|
// 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();
|
ExpressionT result = this->EmptyExpression();
|
||||||
Token::Value token = peek();
|
Token::Value token = peek();
|
||||||
switch (token) {
|
switch (token) {
|
||||||
case Token::THIS: {
|
case Token::THIS: {
|
||||||
Consume(Token::THIS);
|
Consume(Token::THIS);
|
||||||
scope_->RecordThisUsage();
|
scope_->RecordThisUsage();
|
||||||
result = this->ThisExpression(scope_, factory(), pos);
|
result = this->ThisExpression(scope_, factory(), beg_pos);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1875,7 +1876,8 @@ ParserBase<Traits>::ParsePrimaryExpression(bool* ok) {
|
|||||||
case Token::FALSE_LITERAL:
|
case Token::FALSE_LITERAL:
|
||||||
case Token::NUMBER:
|
case Token::NUMBER:
|
||||||
Next();
|
Next();
|
||||||
result = this->ExpressionFromLiteral(token, pos, scanner(), factory());
|
result =
|
||||||
|
this->ExpressionFromLiteral(token, beg_pos, scanner(), factory());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::IDENTIFIER:
|
case Token::IDENTIFIER:
|
||||||
@ -1885,13 +1887,14 @@ ParserBase<Traits>::ParsePrimaryExpression(bool* ok) {
|
|||||||
case Token::FUTURE_STRICT_RESERVED_WORD: {
|
case Token::FUTURE_STRICT_RESERVED_WORD: {
|
||||||
// Using eval or arguments in this context is OK even in strict mode.
|
// Using eval or arguments in this context is OK even in strict mode.
|
||||||
IdentifierT name = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
|
IdentifierT name = ParseIdentifier(kAllowEvalOrArguments, CHECK_OK);
|
||||||
result = this->ExpressionFromIdentifier(name, pos, scope_, factory());
|
result = this->ExpressionFromIdentifier(name, beg_pos, end_pos, scope_,
|
||||||
|
factory());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Token::STRING: {
|
case Token::STRING: {
|
||||||
Consume(Token::STRING);
|
Consume(Token::STRING);
|
||||||
result = this->ExpressionFromString(pos, scanner(), factory());
|
result = this->ExpressionFromString(beg_pos, scanner(), factory());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1918,7 +1921,7 @@ ParserBase<Traits>::ParsePrimaryExpression(bool* ok) {
|
|||||||
// for which an empty parameter list "()" is valid input.
|
// for which an empty parameter list "()" is valid input.
|
||||||
Consume(Token::RPAREN);
|
Consume(Token::RPAREN);
|
||||||
result = this->ParseArrowFunctionLiteral(
|
result = this->ParseArrowFunctionLiteral(
|
||||||
pos, this->EmptyArrowParamList(), CHECK_OK);
|
beg_pos, this->EmptyArrowParamList(), CHECK_OK);
|
||||||
} else {
|
} else {
|
||||||
// Heuristically try to detect immediately called functions before
|
// Heuristically try to detect immediately called functions before
|
||||||
// seeing the call parentheses.
|
// seeing the call parentheses.
|
||||||
@ -1953,8 +1956,8 @@ ParserBase<Traits>::ParsePrimaryExpression(bool* ok) {
|
|||||||
|
|
||||||
case Token::TEMPLATE_SPAN:
|
case Token::TEMPLATE_SPAN:
|
||||||
case Token::TEMPLATE_TAIL:
|
case Token::TEMPLATE_TAIL:
|
||||||
result =
|
result = this->ParseTemplateLiteral(Traits::NoTemplateTag(), beg_pos,
|
||||||
this->ParseTemplateLiteral(Traits::NoTemplateTag(), pos, CHECK_OK);
|
CHECK_OK);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::MOD:
|
case Token::MOD:
|
||||||
@ -2100,7 +2103,8 @@ ParserBase<Traits>::ParsePropertyDefinition(ObjectLiteralCheckerBase* checker,
|
|||||||
bool is_generator = allow_harmony_object_literals_ && Check(Token::MUL);
|
bool is_generator = allow_harmony_object_literals_ && Check(Token::MUL);
|
||||||
|
|
||||||
Token::Value name_token = peek();
|
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(
|
ExpressionT name_expression = ParsePropertyName(
|
||||||
&name, &is_get, &is_set, &name_is_static, is_computed_name,
|
&name, &is_get, &is_set, &name_is_static, is_computed_name,
|
||||||
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
||||||
@ -2195,7 +2199,8 @@ ParserBase<Traits>::ParsePropertyDefinition(ObjectLiteralCheckerBase* checker,
|
|||||||
this->is_generator())) {
|
this->is_generator())) {
|
||||||
DCHECK(!*is_computed_name);
|
DCHECK(!*is_computed_name);
|
||||||
DCHECK(!is_static);
|
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(
|
return factory()->NewObjectLiteralProperty(
|
||||||
name_expression, value, ObjectLiteralProperty::COMPUTED, false, false);
|
name_expression, value, ObjectLiteralProperty::COMPUTED, false, false);
|
||||||
|
|
||||||
|
@ -263,7 +263,12 @@ bool Scope::Analyze(CompilationInfo* info) {
|
|||||||
// Allocate the variables.
|
// Allocate the variables.
|
||||||
{
|
{
|
||||||
AstNodeFactory ast_node_factory(info->ast_value_factory());
|
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
|
#ifdef DEBUG
|
||||||
@ -461,7 +466,7 @@ Variable* Scope::DeclareParameter(const AstRawString* name, VariableMode mode,
|
|||||||
|
|
||||||
|
|
||||||
Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode,
|
Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode,
|
||||||
InitializationFlag init_flag,
|
InitializationFlag init_flag, Variable::Kind kind,
|
||||||
MaybeAssignedFlag maybe_assigned_flag) {
|
MaybeAssignedFlag maybe_assigned_flag) {
|
||||||
DCHECK(!already_resolved());
|
DCHECK(!already_resolved());
|
||||||
// This function handles VAR, LET, and CONST modes. DYNAMIC variables are
|
// This function handles VAR, LET, and CONST modes. DYNAMIC variables are
|
||||||
@ -469,7 +474,7 @@ Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode,
|
|||||||
// explicitly, and TEMPORARY variables are allocated via NewTemporary().
|
// explicitly, and TEMPORARY variables are allocated via NewTemporary().
|
||||||
DCHECK(IsDeclaredVariableMode(mode));
|
DCHECK(IsDeclaredVariableMode(mode));
|
||||||
++num_var_or_const_;
|
++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);
|
maybe_assigned_flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -768,6 +773,20 @@ void Scope::GetNestedScopeChain(Isolate* isolate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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
|
#ifdef DEBUG
|
||||||
static const char* Header(ScopeType scope_type) {
|
static const char* Header(ScopeType scope_type) {
|
||||||
switch (scope_type) {
|
switch (scope_type) {
|
||||||
@ -1050,6 +1069,31 @@ bool Scope::ResolveVariable(CompilationInfo* info, VariableProxy* proxy,
|
|||||||
switch (binding_kind) {
|
switch (binding_kind) {
|
||||||
case BOUND:
|
case BOUND:
|
||||||
// We found a variable binding.
|
// 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;
|
break;
|
||||||
|
|
||||||
case BOUND_EVAL_SHADOWED:
|
case BOUND_EVAL_SHADOWED:
|
||||||
|
21
src/scopes.h
21
src/scopes.h
@ -6,6 +6,7 @@
|
|||||||
#define V8_SCOPES_H_
|
#define V8_SCOPES_H_
|
||||||
|
|
||||||
#include "src/ast.h"
|
#include "src/ast.h"
|
||||||
|
#include "src/pending-compilation-error-handler.h"
|
||||||
#include "src/zone.h"
|
#include "src/zone.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
@ -130,7 +131,7 @@ class Scope: public ZoneObject {
|
|||||||
// Declare a local variable in this scope. If the variable has been
|
// Declare a local variable in this scope. If the variable has been
|
||||||
// declared before, the previously declared variable is returned.
|
// declared before, the previously declared variable is returned.
|
||||||
Variable* DeclareLocal(const AstRawString* name, VariableMode mode,
|
Variable* DeclareLocal(const AstRawString* name, VariableMode mode,
|
||||||
InitializationFlag init_flag,
|
InitializationFlag init_flag, Variable::Kind kind,
|
||||||
MaybeAssignedFlag maybe_assigned_flag = kNotAssigned);
|
MaybeAssignedFlag maybe_assigned_flag = kNotAssigned);
|
||||||
|
|
||||||
// Declare an implicit global variable in this scope which must be a
|
// Declare an implicit global variable in this scope which must be a
|
||||||
@ -142,12 +143,14 @@ class Scope: public ZoneObject {
|
|||||||
// Create a new unresolved variable.
|
// Create a new unresolved variable.
|
||||||
VariableProxy* NewUnresolved(AstNodeFactory* factory,
|
VariableProxy* NewUnresolved(AstNodeFactory* factory,
|
||||||
const AstRawString* name,
|
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
|
// Note that we must not share the unresolved variables with
|
||||||
// the same name because they may be removed selectively via
|
// the same name because they may be removed selectively via
|
||||||
// RemoveUnresolved().
|
// RemoveUnresolved().
|
||||||
DCHECK(!already_resolved());
|
DCHECK(!already_resolved());
|
||||||
VariableProxy* proxy = factory->NewVariableProxy(name, false, position);
|
VariableProxy* proxy =
|
||||||
|
factory->NewVariableProxy(name, false, start_position, end_position);
|
||||||
unresolved_.Add(proxy, zone_);
|
unresolved_.Add(proxy, zone_);
|
||||||
return proxy;
|
return proxy;
|
||||||
}
|
}
|
||||||
@ -317,6 +320,12 @@ class Scope: public ZoneObject {
|
|||||||
// Does any inner scope access "this".
|
// Does any inner scope access "this".
|
||||||
bool inner_uses_this() const { return inner_scope_uses_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.
|
// Accessors.
|
||||||
|
|
||||||
@ -470,6 +479,10 @@ class Scope: public ZoneObject {
|
|||||||
return params_.Contains(variables_.Lookup(name));
|
return params_.Contains(variables_.Lookup(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error handling.
|
||||||
|
void ReportMessage(int start_position, int end_position, const char* message,
|
||||||
|
const AstRawString* arg);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Debugging.
|
// Debugging.
|
||||||
|
|
||||||
@ -696,6 +709,8 @@ class Scope: public ZoneObject {
|
|||||||
|
|
||||||
AstValueFactory* ast_value_factory_;
|
AstValueFactory* ast_value_factory_;
|
||||||
Zone* zone_;
|
Zone* zone_;
|
||||||
|
|
||||||
|
PendingCompilationErrorHandler pending_error_handler_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} } // namespace v8::internal
|
} } // namespace v8::internal
|
||||||
|
@ -18,7 +18,7 @@ namespace internal {
|
|||||||
|
|
||||||
class Variable: public ZoneObject {
|
class Variable: public ZoneObject {
|
||||||
public:
|
public:
|
||||||
enum Kind { NORMAL, THIS, NEW_TARGET, ARGUMENTS };
|
enum Kind { NORMAL, FUNCTION, THIS, NEW_TARGET, ARGUMENTS };
|
||||||
|
|
||||||
enum Location {
|
enum Location {
|
||||||
// Before and during variable allocation, a variable whose location is
|
// Before and during variable allocation, a variable whose location is
|
||||||
@ -98,6 +98,7 @@ class Variable: public ZoneObject {
|
|||||||
return initialization_flag_ == kNeedsInitialization;
|
return initialization_flag_ == kNeedsInitialization;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_function() const { return kind_ == FUNCTION; }
|
||||||
bool is_this() const { return kind_ == THIS; }
|
bool is_this() const { return kind_ == THIS; }
|
||||||
bool is_new_target() const { return kind_ == NEW_TARGET; }
|
bool is_new_target() const { return kind_ == NEW_TARGET; }
|
||||||
bool is_arguments() const { return kind_ == ARGUMENTS; }
|
bool is_arguments() const { return kind_ == ARGUMENTS; }
|
||||||
|
188
test/mjsunit/strong/declaration-after-use.js
Normal file
188
test/mjsunit/strong/declaration-after-use.js
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
// 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();
|
||||||
|
})();
|
Loading…
Reference in New Issue
Block a user