Skip to content

Commit

Permalink
Disallow redeclaration with function declarations in blocks.
Browse files Browse the repository at this point in the history
Fixes #2755

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=179079879
  • Loading branch information
tbreisacher authored and brad4d committed Dec 15, 2017
1 parent 073ee36 commit f5e3ed7
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 27 deletions.
19 changes: 15 additions & 4 deletions src/com/google/javascript/jscomp/VarCheck.java
Expand Up @@ -81,10 +81,10 @@ class VarCheck extends AbstractPostOrderCallback implements
"JSC_VAR_ARGUMENTS_SHADOWED_ERROR",
"Shadowing \"arguments\" is not allowed");

static final DiagnosticType LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR =
static final DiagnosticType BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR =
DiagnosticType.error(
"JSC_LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR",
"Duplicate let / const / class declaration in the same scope is not allowed.");
"JSC_BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR",
"Duplicate let / const / class / function declaration in the same scope is not allowed.");

// The arguments variable is special, in that it's declared in every local
// scope, but not explicitly declared.
Expand Down Expand Up @@ -412,7 +412,18 @@ public void onRedeclaration(
|| parent.isClass()
|| (origParent != null
&& (origParent.isLet() || origParent.isConst() || origParent.isClass()))) {
compiler.report(JSError.make(n, LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR));
compiler.report(JSError.make(n, BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR));
return;
} else if (parent.isFunction()
// Redeclarations of functions in global scope are fairly common, so allow them
// (at least for now).
&& !s.isGlobal()
&& origParent != null
&& (origParent.isFunction()
|| origParent.isLet()
|| origParent.isConst()
|| origParent.isClass())) {
compiler.report(JSError.make(n, BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR));
return;
}

Expand Down
79 changes: 56 additions & 23 deletions test/com/google/javascript/jscomp/VarCheckTest.java
Expand Up @@ -18,6 +18,7 @@

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.javascript.jscomp.VarCheck.BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR;
import static com.google.javascript.jscomp.VarCheck.VAR_MULTIPLY_DECLARED_ERROR;

import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -156,26 +157,26 @@ public void testMultiplyDeclaredVars4() {
}

public void testMultiplyDeclaredLets() {
testError("let x = 1; let x = 2;", VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
testError("let x = 1; var x = 2;", VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
testError("var x = 1; let x = 2;", VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
testError("let x = 1; let x = 2;", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError("let x = 1; var x = 2;", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError("var x = 1; let x = 2;", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
}

public void testMultiplyDeclaredConsts() {
testError("const x = 1; const x = 2;", VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
testError("const x = 1; var x = 2;", VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
testError("var x = 1; const x = 2;", VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
testError("const x = 1; const x = 2;", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError("const x = 1; var x = 2;", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError("var x = 1; const x = 2;", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
}

public void testMultiplyDeclaredConsts_withES6Modules() {
testError("export function f() { const x = 1; const x = 2; }",
VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);

testError("export const x = 1; export var x = 2;",
VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);

testError("export const a = 1, a = 2;",
VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
}

public void testMultiplyDeclareLetsInDifferentScope() {
Expand All @@ -184,12 +185,12 @@ public void testMultiplyDeclareLetsInDifferentScope() {
}

public void testReferencedVarDefinedClass() {
testError("var x; class x{ }", VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
testError("let x; class x{ }", VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
testError("const x = 1; class x{ }", VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
testError("class x{ } let x;", VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
testError("var x; class x{ }", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError("let x; class x{ }", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError("const x = 1; class x{ }", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError("class x{ } let x;", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError("export default class x{ } let x;",
VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
}

public void testNamedClass() {
Expand Down Expand Up @@ -575,11 +576,11 @@ public void testDuplicateVar() {
public void testDontAllowSuppressDupeOnLet() {
testError(
"let a; /** @suppress {duplicate} */ let a; ",
VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);

testError(
"function f() { let a; /** @suppress {duplicate} */ let a; }",
VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
}

public void testDuplicateBlockScopedDeclarationInSwitch() {
Expand All @@ -595,7 +596,7 @@ public void testDuplicateBlockScopedDeclarationInSwitch() {
" break;",
" }",
"}"),
VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);

testError(
lines(
Expand All @@ -609,20 +610,20 @@ public void testDuplicateBlockScopedDeclarationInSwitch() {
" break;",
" }",
"}"),
VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
}

public void testLetConstRedeclareWithFunctions_withEs6Modules() {
testError("function f() {} let f = 1; export {f};",
VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError("let f = 1; function f() {} export {f};",
VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError("const f = 1; function f() {} export {f};",
VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError("function f() {} const f = 1; export {f};",
VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError("export default function f() {}; let f = 5;",
VarCheck.LET_CONST_CLASS_MULTIPLY_DECLARED_ERROR);
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
}

public void testFunctionScopeArguments() {
Expand All @@ -637,6 +638,38 @@ public void testFunctionScopeArguments() {
testSame("function f() {var arguments}");
}

public void testFunctionRedeclared_global() {
// Redeclaration in global scope is allowed.
testSame("/** @fileoverview @suppress {duplicate} */ function f() {};function f() {};");
}

public void testFunctionRedeclared1() {
testError("{ function f() {}; function f() {}; }", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError(
"if (0) { function f() {}; function f() {}; }", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError(
"try { function f() {}; function f() {}; } catch (e) {}",
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
}

public void testFunctionRedeclared2() {
testError("{ let f = 0; function f() {}; }", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError(
"if (0) { const f = 1; function f() {}; }", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError(
"try { class f {}; function f() {}; } catch (e) {}",
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
}

public void testFunctionRedeclared3() {
testError("{ function f() {}; let f = 0; }", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError(
"if (0) { function f() {}; const f = 0;}", BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
testError(
"try { function f() {}; class f {}; } catch (e) {}",
BLOCK_SCOPED_DECL_MULTIPLY_DECLARED_ERROR);
}

public void testNoUndeclaredVarWhenUsingClosurePass() {
enableClosurePass();
// We don't want to get goog as an undeclared var here.
Expand Down

0 comments on commit f5e3ed7

Please sign in to comment.