--- a/js/src/frontend/NameAnalysisTypes.h
+++ b/js/src/frontend/NameAnalysisTypes.h
@@ -119,36 +119,44 @@ static inline bool
DeclarationKindIsLexical(DeclarationKind kind)
{
return BindingKindIsLexical(DeclarationKindToBindingKind(kind));
}
// Used in Parser to track declared names.
class DeclaredNameInfo
{
+ uint32_t pos_;
DeclarationKind kind_;
// If the declared name is a binding, whether the binding is closed
// over. Its value is meaningless if the declared name is not a binding
// (i.e., a 'var' declared name in a non-var scope).
bool closedOver_;
public:
- explicit DeclaredNameInfo(DeclarationKind kind)
- : kind_(kind),
+ explicit DeclaredNameInfo(DeclarationKind kind, uint32_t pos)
+ : pos_(pos),
+ kind_(kind),
closedOver_(false)
{ }
// Needed for InlineMap.
DeclaredNameInfo() = default;
DeclarationKind kind() const {
return kind_;
}
+ static const uint32_t npos = uint32_t(-1);
+
+ uint32_t pos() const {
+ return pos_;
+ }
+
void alterKind(DeclarationKind kind) {
kind_ = kind;
}
void setClosedOver() {
closedOver_ = true;
}
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -14,16 +14,18 @@
* syntax trees, see Parser.h). After tree construction, it rewrites trees to
* fold constants and evaluate compile-time expressions.
*
* This parser attempts no error recovery.
*/
#include "frontend/Parser.h"
+#include "mozilla/Sprintf.h"
+
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsfun.h"
#include "jsopcode.h"
#include "jsscript.h"
#include "jstypes.h"
@@ -193,21 +195,22 @@ DeclarationKindIsCatchParameter(Declarat
bool
ParseContext::Scope::addCatchParameters(ParseContext* pc, Scope& catchParamScope)
{
if (pc->useAsmOrInsideUseAsm())
return true;
for (DeclaredNameMap::Range r = catchParamScope.declared_->all(); !r.empty(); r.popFront()) {
DeclarationKind kind = r.front().value()->kind();
+ uint32_t pos = r.front().value()->pos();
MOZ_ASSERT(DeclarationKindIsCatchParameter(kind));
JSAtom* name = r.front().key();
AddDeclaredNamePtr p = lookupDeclaredNameForAdd(name);
MOZ_ASSERT(!p);
- if (!addDeclaredName(pc, p, name, kind))
+ if (!addDeclaredName(pc, p, name, kind, pos))
return false;
}
return true;
}
void
ParseContext::Scope::removeCatchParameters(ParseContext* pc, Scope& catchParamScope)
@@ -336,17 +339,18 @@ ParseContext::init()
RootedFunction fun(cx, functionBox()->function());
if (fun->isNamedLambda()) {
if (!namedLambdaScope_->init(this))
return false;
AddDeclaredNamePtr p =
namedLambdaScope_->lookupDeclaredNameForAdd(fun->explicitName());
MOZ_ASSERT(!p);
if (!namedLambdaScope_->addDeclaredName(this, p, fun->explicitName(),
- DeclarationKind::Const))
+ DeclarationKind::Const,
+ DeclaredNameInfo::npos))
{
return false;
}
}
if (!functionScope_->init(this))
return false;
@@ -989,37 +993,67 @@ Parser<ParseHandler>::hasValidSimpleStri
if (!isValidStrictBinding(name->asPropertyName()))
return false;
}
return true;
}
template <typename ParseHandler>
void
-Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKind kind,
- TokenPos pos)
+Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind,
+ TokenPos pos, uint32_t prevPos)
{
JSAutoByteString bytes;
if (!AtomToPrintableString(context, name, &bytes))
return;
- errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(kind), bytes.ptr());
+
+ if (prevPos == DeclaredNameInfo::npos) {
+ errorAt(pos.begin, JSMSG_REDECLARED_VAR, DeclarationKindString(prevKind), bytes.ptr());
+ return;
+ }
+
+ auto notes = MakeUnique<JSErrorNotes>();
+ if (!notes)
+ return;
+
+ uint32_t line, column;
+ tokenStream.srcCoords.lineNumAndColumnIndex(prevPos, &line, &column);
+
+ const size_t MaxWidth = sizeof("4294967295");
+ char columnNumber[MaxWidth];
+ SprintfLiteral(columnNumber, "%" PRIu32, column);
+ char lineNumber[MaxWidth];
+ SprintfLiteral(lineNumber, "%" PRIu32, line);
+
+ if (!notes->addNoteLatin1(pc->sc()->context,
+ getFilename(), line, column,
+ GetErrorMessage, nullptr,
+ JSMSG_REDECLARED_PREV,
+ lineNumber, columnNumber))
+ {
+ return;
+ }
+
+ errorWithNotesAt(Move(notes), pos.begin, JSMSG_REDECLARED_VAR,
+ DeclarationKindString(prevKind), bytes.ptr());
}
// notePositionalFormalParameter is called for both the arguments of a regular
// function definition and the arguments specified by the Function
// constructor.
//
// The 'disallowDuplicateParams' bool indicates whether the use of another
// feature (destructuring or default arguments) disables duplicate arguments.
// (ECMA-262 requires us to support duplicate parameter names, but, for newer
// features, we consider the code to have "opted in" to higher standards and
// forbid duplicates.)
template <typename ParseHandler>
bool
Parser<ParseHandler>::notePositionalFormalParameter(Node fn, HandlePropertyName name,
+ uint32_t beginPos,
bool disallowDuplicateParams,
bool* duplicatedParam)
{
AutoTraceLog traceLog(TraceLoggerForCurrentThread(context), TraceLogger_FrontendNameAnalysis);
if (AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name)) {
if (disallowDuplicateParams) {
error(JSMSG_BAD_DUP_ARGS);
@@ -1036,17 +1070,17 @@ Parser<ParseHandler>::notePositionalForm
return false;
if (!strictModeError(JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
return false;
}
*duplicatedParam = true;
} else {
DeclarationKind kind = DeclarationKind::PositionalFormalParameter;
- if (!pc->functionScope().addDeclaredName(pc, p, name, kind))
+ if (!pc->functionScope().addDeclaredName(pc, p, name, kind, beginPos))
return false;
}
if (!pc->positionalFormalParameterNames().append(name)) {
ReportOutOfMemory(context);
return false;
}
@@ -1140,18 +1174,18 @@ static bool
DeclarationKindIsParameter(DeclarationKind kind)
{
return kind == DeclarationKind::PositionalFormalParameter ||
kind == DeclarationKind::FormalParameter;
}
template <typename ParseHandler>
bool
-Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
- Maybe<DeclarationKind>* redeclaredKind)
+Parser<ParseHandler>::tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
+ Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos)
{
MOZ_ASSERT(DeclarationKindIsVar(kind));
// It is an early error if a 'var' declaration appears inside a
// scope contour that has a lexical declaration of the same name. For
// example, the following are early errors:
//
// { let x; var x; }
@@ -1214,47 +1248,57 @@ Parser<ParseHandler>::tryDeclareVar(Hand
// Annex B.3.3 allows redeclaring functions in the same block.
bool annexB33Allowance = declaredKind == DeclarationKind::LexicalFunction &&
kind == DeclarationKind::VarForAnnexBLexicalFunction &&
scope == pc->innermostScope();
if (!annexB35Allowance && !annexB33Allowance) {
*redeclaredKind = Some(declaredKind);
+ *prevPos = p->value()->pos();
return true;
}
} else if (kind == DeclarationKind::VarForAnnexBLexicalFunction) {
MOZ_ASSERT(DeclarationKindIsParameter(declaredKind));
// Annex B.3.3.1 disallows redeclaring parameter names.
+ // We don't need to set *prevPos here since this case is not
+ // an error.
*redeclaredKind = Some(declaredKind);
return true;
}
} else {
- if (!scope->addDeclaredName(pc, p, name, kind))
+ if (!scope->addDeclaredName(pc, p, name, kind, beginPos))
return false;
}
}
- if (!pc->sc()->strict() && pc->sc()->isEvalContext())
+ if (!pc->sc()->strict() && pc->sc()->isEvalContext()) {
*redeclaredKind = isVarRedeclaredInEval(name, kind);
+ // We don't have position information at runtime.
+ *prevPos = DeclaredNameInfo::npos;
+ }
return true;
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name,
- bool* tryAnnexB)
+ uint32_t beginPos, bool* tryAnnexB)
{
AutoTraceLog traceLog(TraceLoggerForCurrentThread(context), TraceLogger_FrontendNameAnalysis);
Maybe<DeclarationKind> redeclaredKind;
- if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, &redeclaredKind))
+ uint32_t unused;
+ if (!tryDeclareVar(name, DeclarationKind::VarForAnnexBLexicalFunction, beginPos,
+ &redeclaredKind, &unused))
+ {
return false;
+ }
if (!redeclaredKind && pc->isFunctionBox()) {
ParseContext::Scope& funScope = pc->functionScope();
ParseContext::Scope& varScope = pc->varScope();
if (&funScope != &varScope) {
// Annex B.3.3.1 disallows redeclaring parameter names. In the
// presence of parameter expressions, parameter names are on the
// function scope, which encloses the var scope. This means
@@ -1318,38 +1362,39 @@ Parser<ParseHandler>::noteDeclaredName(H
if (pc->useAsmOrInsideUseAsm())
return true;
switch (kind) {
case DeclarationKind::Var:
case DeclarationKind::BodyLevelFunction:
case DeclarationKind::ForOfVar: {
Maybe<DeclarationKind> redeclaredKind;
- if (!tryDeclareVar(name, kind, &redeclaredKind))
+ uint32_t prevPos;
+ if (!tryDeclareVar(name, kind, pos.begin, &redeclaredKind, &prevPos))
return false;
if (redeclaredKind) {
- reportRedeclaration(name, *redeclaredKind, pos);
+ reportRedeclaration(name, *redeclaredKind, pos, prevPos);
return false;
}
break;
}
case DeclarationKind::FormalParameter: {
// It is an early error if any non-positional formal parameter name
// (e.g., destructuring formal parameter) is duplicated.
AddDeclaredNamePtr p = pc->functionScope().lookupDeclaredNameForAdd(name);
if (p) {
error(JSMSG_BAD_DUP_ARGS);
return false;
}
- if (!pc->functionScope().addDeclaredName(pc, p, name, kind))
+ if (!pc->functionScope().addDeclaredName(pc, p, name, kind, pos.begin))
return false;
break;
}
case DeclarationKind::LexicalFunction: {
// Functions in block have complex allowances in sloppy mode for being
// labelled that other lexical declarations do not have. Those checks
@@ -1362,25 +1407,25 @@ Parser<ParseHandler>::noteDeclaredName(H
// with the same name in the same scope.
//
// In sloppy mode, lexical functions may redeclare other lexical
// functions for web compatibility reasons.
if (pc->sc()->strict() ||
(p->value()->kind() != DeclarationKind::LexicalFunction &&
p->value()->kind() != DeclarationKind::VarForAnnexBLexicalFunction))
{
- reportRedeclaration(name, p->value()->kind(), pos);
+ reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
// Update the DeclarationKind to make a LexicalFunction
// declaration that shadows the VarForAnnexBLexicalFunction.
p->value()->alterKind(kind);
} else {
- if (!scope->addDeclaredName(pc, p, name, kind))
+ if (!scope->addDeclaredName(pc, p, name, kind, pos.begin))
return false;
}
break;
}
case DeclarationKind::Let:
case DeclarationKind::Const:
@@ -1410,39 +1455,39 @@ Parser<ParseHandler>::noteDeclaredName(H
// For body-level lexically declared names in a function, it is an
// early error if there is a formal parameter of the same name. This
// needs a special check if there is an extra var scope due to
// parameter expressions.
if (pc->isFunctionExtraBodyVarScopeInnermost()) {
DeclaredNamePtr p = pc->functionScope().lookupDeclaredName(name);
if (p && DeclarationKindIsParameter(p->value()->kind())) {
- reportRedeclaration(name, p->value()->kind(), pos);
+ reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
}
// It is an early error if there is another declaration with the same
// name in the same scope.
AddDeclaredNamePtr p = scope->lookupDeclaredNameForAdd(name);
if (p) {
// If the early error would have occurred due to Annex B.3.3
// semantics, remove the synthesized Annex B var declaration, do
// not report the redeclaration, and declare the lexical name.
if (p->value()->kind() == DeclarationKind::VarForAnnexBLexicalFunction) {
ParseContext::Scope::removeVarForAnnexBLexicalFunction(pc, name);
p = scope->lookupDeclaredNameForAdd(name);
MOZ_ASSERT(!p);
} else {
- reportRedeclaration(name, p->value()->kind(), pos);
+ reportRedeclaration(name, p->value()->kind(), pos, p->value()->pos());
return false;
}
}
- if (!p && !scope->addDeclaredName(pc, p, name, kind))
+ if (!p && !scope->addDeclaredName(pc, p, name, kind, pos.begin))
return false;
break;
}
case DeclarationKind::CoverArrowParameter:
// CoverArrowParameter is only used as a placeholder declaration kind.
break;
@@ -2182,18 +2227,21 @@ Parser<ParseHandler>::declareFunctionThi
declareThis = funbox->function()->lazyScript()->hasThisBinding();
else
declareThis = hasUsedFunctionSpecialName(dotThis) || funbox->isDerivedClassConstructor();
if (declareThis) {
ParseContext::Scope& funScope = pc->functionScope();
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotThis);
MOZ_ASSERT(!p);
- if (!funScope.addDeclaredName(pc, p, dotThis, DeclarationKind::Var))
+ if (!funScope.addDeclaredName(pc, p, dotThis, DeclarationKind::Var,
+ DeclaredNameInfo::npos))
+ {
return false;
+ }
funbox->setHasThisBinding();
}
return true;
}
template <typename ParseHandler>
typename ParseHandler::Node
@@ -2225,18 +2273,21 @@ template <typename ParseHandler>
bool
Parser<ParseHandler>::declareDotGeneratorName()
{
// The special '.generator' binding must be on the function scope, as
// generators expect to find it on the CallObject.
ParseContext::Scope& funScope = pc->functionScope();
HandlePropertyName dotGenerator = context->names().dotGenerator;
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotGenerator);
- if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var))
+ if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var,
+ DeclaredNameInfo::npos))
+ {
return false;
+ }
return true;
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::finishFunctionScopes(bool isStandaloneFunction)
{
FunctionBox* funbox = pc->functionBox();
@@ -2449,18 +2500,21 @@ Parser<ParseHandler>::declareFunctionArg
tryDeclareArguments = true;
else
funbox->usesArguments = true;
}
if (tryDeclareArguments) {
AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(argumentsName);
if (!p) {
- if (!funScope.addDeclaredName(pc, p, argumentsName, DeclarationKind::Var))
+ if (!funScope.addDeclaredName(pc, p, argumentsName, DeclarationKind::Var,
+ DeclaredNameInfo::npos))
+ {
return false;
+ }
funbox->declaredArguments = true;
funbox->usesArguments = true;
} else if (hasExtraBodyVarScope) {
// Formal parameters shadow the arguments object.
return true;
}
}
@@ -2932,18 +2986,18 @@ Parser<ParseHandler>::functionArguments(
if (parenFreeArrow)
funbox->setStart(tokenStream);
RootedPropertyName name(context, bindingIdentifier(yieldHandling));
if (!name)
return false;
- if (!notePositionalFormalParameter(funcpn, name, disallowDuplicateParams,
- &duplicatedParam))
+ if (!notePositionalFormalParameter(funcpn, name, pos().begin,
+ disallowDuplicateParams, &duplicatedParam))
{
return false;
}
if (duplicatedParam)
funbox->hasDuplicateParameters = true;
break;
}
@@ -3652,17 +3706,17 @@ Parser<ParseHandler>::functionStmt(Yield
MOZ_ASSERT(StatementKindIsBraced(declaredInStmt->kind()));
if (!pc->sc()->strict() && generatorKind == NotGenerator) {
// Under sloppy mode, try Annex B.3.3 semantics. If making an
// additional 'var' binding of the same name does not throw an
// early error, do so. This 'var' binding would be assigned
// the function object when its declaration is reached, not at
// the start of the block.
- if (!tryDeclareVarForAnnexBLexicalFunction(name, &tryAnnexB))
+ if (!tryDeclareVarForAnnexBLexicalFunction(name, pos().begin, &tryAnnexB))
return null();
}
if (!noteDeclaredName(name, DeclarationKind::LexicalFunction, pos()))
return null();
} else {
if (!noteDeclaredName(name, DeclarationKind::BodyLevelFunction, pos()))
return null();
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -142,19 +142,19 @@ class ParseContext : public Nestable<Par
return declared_->lookup(name);
}
AddDeclaredNamePtr lookupDeclaredNameForAdd(JSAtom* name) {
return declared_->lookupForAdd(name);
}
MOZ_MUST_USE bool addDeclaredName(ParseContext* pc, AddDeclaredNamePtr& p, JSAtom* name,
- DeclarationKind kind)
+ DeclarationKind kind, uint32_t pos)
{
- return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind)));
+ return maybeReportOOM(pc, declared_->add(p, name, DeclaredNameInfo(kind, pos)));
}
// Remove all VarForAnnexBLexicalFunction declarations of a certain
// name from all scopes in pc's scope stack.
static void removeVarForAnnexBLexicalFunction(ParseContext* pc, JSAtom* name);
// Add and remove catch parameter names. Used to implement the odd
// semantics of catch bodies.
@@ -1437,25 +1437,27 @@ class Parser final : public ParserBase,
FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls);
private:
bool checkIncDecOperand(Node operand, uint32_t operandOffset);
bool checkStrictAssignment(Node lhs);
bool hasValidSimpleStrictParameterNames();
- void reportRedeclaration(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
- bool notePositionalFormalParameter(Node fn, HandlePropertyName name,
+ void reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, TokenPos pos,
+ uint32_t prevPos);
+ bool notePositionalFormalParameter(Node fn, HandlePropertyName name, uint32_t beginPos,
bool disallowDuplicateParams, bool* duplicatedParam);
bool noteDestructuredPositionalFormalParameter(Node fn, Node destruct);
mozilla::Maybe<DeclarationKind> isVarRedeclaredInEval(HandlePropertyName name,
DeclarationKind kind);
- bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind,
- mozilla::Maybe<DeclarationKind>* redeclaredKind);
- bool tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, bool* tryAnnexB);
+ bool tryDeclareVar(HandlePropertyName name, DeclarationKind kind, uint32_t beginPos,
+ mozilla::Maybe<DeclarationKind>* redeclaredKind, uint32_t* prevPos);
+ bool tryDeclareVarForAnnexBLexicalFunction(HandlePropertyName name, uint32_t beginPos,
+ bool* tryAnnexB);
bool checkLexicalDeclarationDirectlyWithinBlock(ParseContext::Statement& stmt,
DeclarationKind kind, TokenPos pos);
bool noteDeclaredName(HandlePropertyName name, DeclarationKind kind, TokenPos pos);
bool noteUsedName(HandlePropertyName name);
bool hasUsedName(HandlePropertyName name);
// Required on Scope exit.
bool propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/redeclaration.js
@@ -0,0 +1,230 @@
+// Error message for redeclaration should show the position where the variable
+// was declared.
+
+const npos = -1;
+
+function test_one(fun, filename, name,
+ [prevLineNumber, prevColumnNumber],
+ [lineNumber, columnNumber]) {
+ let caught = false;
+ try {
+ fun();
+ } catch (e) {
+ assertEq(e.message.includes("redeclaration"), true);
+ assertEq(e.lineNumber, lineNumber);
+ assertEq(e.columnNumber, columnNumber);
+ let notes = getErrorNotes(e);
+ if (prevLineNumber == npos) {
+ assertEq(notes.length, 0);
+ } else {
+ assertEq(notes.length, 1);
+ let note = notes[0];
+ assertEq(note.message,
+ `Previously declared at line ${prevLineNumber}, column ${prevColumnNumber}`);
+ assertEq(note.lineNumber, prevLineNumber);
+ assertEq(note.columnNumber, prevColumnNumber);
+ if (filename)
+ assertEq(note.fileName, filename);
+ }
+ caught = true;
+ }
+ assertEq(caught, true);
+}
+
+function test_parse(source, ...args) {
+ test_one(() => {
+ Reflect.parse(source, { source: "foo.js" });
+ }, "foo.js", ...args);
+}
+
+function test_eval(source, ...args) {
+ test_one(() => {
+ eval(source);
+ }, undefined, ...args);
+}
+
+function test(...args) {
+ test_parse(...args);
+ test_eval(...args);
+}
+
+// let
+
+test(`
+let a, a;
+`, "a", [2, 4], [2, 7]);
+
+test(`
+let a;
+let a;
+`, "a", [2, 4], [3, 4]);
+
+test(`
+let a;
+const a = 1;
+`, "a", [2, 4], [3, 6]);
+
+test(`
+let a;
+var a;
+`, "a", [2, 4], [3, 4]);
+
+test(`
+let a;
+function a() {
+}
+`, "a", [2, 4], [3, 9]);
+
+test(`
+{
+ let a;
+ function a() {
+ }
+}
+`, "a", [3, 6], [4, 11]);
+
+// const
+
+test(`
+const a = 1, a = 2;
+`, "a", [2, 6], [2, 13]);
+
+test(`
+const a = 1;
+const a = 2;
+`, "a", [2, 6], [3, 6]);
+
+test(`
+const a = 1;
+let a;
+`, "a", [2, 6], [3, 4]);
+
+test(`
+const a = 1;
+var a;
+`, "a", [2, 6], [3, 4]);
+
+test(`
+const a = 1;
+function a() {
+}
+`, "a", [2, 6], [3, 9]);
+
+test(`
+{
+ const a = 1;
+ function a() {
+ }
+}
+`, "a", [3, 8], [4, 11]);
+
+// var
+
+test(`
+var a;
+let a;
+`, "a", [2, 4], [3, 4]);
+
+test(`
+var a;
+const a = 1;
+`, "a", [2, 4], [3, 6]);
+
+// function
+
+test(`
+function a() {};
+let a;
+`, "a", [2, 9], [3, 4]);
+
+test(`
+function a() {};
+const a = 1;
+`, "a", [2, 9], [3, 6]);
+
+// Annex B lexical function
+
+test(`
+{
+ function a() {};
+ let a;
+}
+`, "a", [3, 11], [4, 6]);
+
+test(`
+{
+ function a() {};
+ const a = 1;
+}
+`, "a", [3, 11], [4, 8]);
+
+// catch parameter
+
+test(`
+try {
+} catch (a) {
+ let a;
+}
+`, "a", [3, 9], [4, 6]);
+
+test(`
+try {
+} catch (a) {
+ const a = 1;
+}
+`, "a", [3, 9], [4, 8]);
+
+test(`
+try {
+} catch (a) {
+ function a() {
+ }
+}
+`, "a", [3, 9], [4, 11]);
+
+// parameter
+
+test(`
+function f(a) {
+ let a;
+}
+`, "a", [2, 11], [3, 6]);
+
+test(`
+function f(a) {
+ const a = 1;
+}
+`, "a", [2, 11], [3, 8]);
+
+test(`
+function f([a]) {
+ let a;
+}
+`, "a", [2, 12], [3, 6]);
+
+test(`
+function f({a}) {
+ let a;
+}
+`, "a", [2, 12], [3, 6]);
+
+test(`
+function f(...a) {
+ let a;
+}
+`, "a", [2, 14], [3, 6]);
+
+test(`
+function f(a=1) {
+ let a;
+}
+`, "a", [2, 11], [3, 6]);
+
+// eval
+// We don't have position information at runtime.
+// No note should be shown.
+
+test_eval(`
+let a;
+eval("var a");
+`, "a", [npos, npos], [1, 4]);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -56,16 +56,17 @@ MSG_DEF(JSMSG_TOPRIMITIVE_NOT_CALLABLE,
MSG_DEF(JSMSG_TOPRIMITIVE_RETURNED_OBJECT, 2, JSEXN_TYPEERR, "can't convert {0} to {1}: its [Symbol.toPrimitive] method returned an object")
MSG_DEF(JSMSG_NO_PROPERTIES, 1, JSEXN_TYPEERR, "{0} has no properties")
MSG_DEF(JSMSG_BAD_REGEXP_FLAG, 1, JSEXN_SYNTAXERR, "invalid regular expression flag {0}")
MSG_DEF(JSMSG_ARG_INDEX_OUT_OF_RANGE, 1, JSEXN_RANGEERR, "argument {0} accesses an index that is out of range")
MSG_DEF(JSMSG_SPREAD_TOO_LARGE, 0, JSEXN_RANGEERR, "array too large due to spread operand(s)")
MSG_DEF(JSMSG_BAD_WEAKMAP_KEY, 0, JSEXN_TYPEERR, "cannot use the given object as a weak map key")
MSG_DEF(JSMSG_BAD_GETTER_OR_SETTER, 1, JSEXN_TYPEERR, "invalid {0} usage")
MSG_DEF(JSMSG_BAD_ARRAY_LENGTH, 0, JSEXN_RANGEERR, "invalid array length")
+MSG_DEF(JSMSG_REDECLARED_PREV, 2, JSEXN_NOTE, "Previously declared at line {0}, column {1}")
MSG_DEF(JSMSG_REDECLARED_VAR, 2, JSEXN_SYNTAXERR, "redeclaration of {0} {1}")
MSG_DEF(JSMSG_UNDECLARED_VAR, 1, JSEXN_REFERENCEERR, "assignment to undeclared variable {0}")
MSG_DEF(JSMSG_GETTER_ONLY, 0, JSEXN_TYPEERR, "setting a property that has only a getter")
MSG_DEF(JSMSG_OVERWRITING_ACCESSOR, 1, JSEXN_TYPEERR, "can't overwrite accessor property {0}")
MSG_DEF(JSMSG_UNDEFINED_PROP, 1, JSEXN_REFERENCEERR, "reference to undefined property {0}")
MSG_DEF(JSMSG_INVALID_MAP_ITERABLE, 1, JSEXN_TYPEERR, "iterable for {0} should have array-like objects")
MSG_DEF(JSMSG_NESTING_GENERATOR, 0, JSEXN_TYPEERR, "already executing generator")
MSG_DEF(JSMSG_INCOMPATIBLE_METHOD, 3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}")