Skip to content

Commit

Permalink
Update: no-shadow-restricted-names lets unassigned vars shadow undefi…
Browse files Browse the repository at this point in the history
…ned (#11341)

This updates the `no-shadow-restricted-names` to allow a variable to shadow `undefined` if that variable is never assigned a value (i.e. if the variable always actually has a value of `undefined`. The declaration `var undefined;` is occasionally used in pre-ES5 environments where the `undefined` global is mutable, to ensure that the identifier `undefined` can reliably be used even if someone else tampered with the global. In general, a `var undefined;` declaration doesn't really violate the spirit of the rule, because it doesn't change the value of the identifier `undefined`.

The goal of this change is to eliminate a false positive for codebases that support pre-ES5 environments, so that the rule can be added to `eslint:recommended` in the future.
  • Loading branch information
not-an-aardvark committed Feb 6, 2019
1 parent d0e823a commit 533d240
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 5 deletions.
5 changes: 4 additions & 1 deletion docs/rules/no-shadow-restricted-names.md
Expand Up @@ -19,7 +19,7 @@ function NaN(){}

!function(Infinity){};

var undefined;
var undefined = 5;

try {} catch(eval){}
```
Expand All @@ -32,6 +32,9 @@ Examples of **correct** code for this rule:
var Object;

function f(a, b){}

// Exception: `undefined` may be shadowed if the variable is never assigned a value.
var undefined;
```

## Further Reading
Expand Down
18 changes: 16 additions & 2 deletions lib/rules/no-shadow-restricted-names.js
Expand Up @@ -4,6 +4,19 @@
*/
"use strict";

/**
* Determines if a variable safely shadows undefined.
* This is the case when a variable named `undefined` is never assigned to a value (i.e. it always shares the same value
* as the global).
* @param {eslintScope.Variable} variable The variable to check
* @returns {boolean} true if this variable safely shadows `undefined`
*/
function safelyShadowsUndefined(variable) {
return variable.name === "undefined" &&
variable.references.every(ref => !ref.isWrite()) &&
variable.defs.every(def => def.node.type === "VariableDeclarator" && def.node.init === null);
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
Expand All @@ -24,12 +37,13 @@ module.exports = {

create(context) {

const RESTRICTED = ["undefined", "NaN", "Infinity", "arguments", "eval"];

const RESTRICTED = new Set(["undefined", "NaN", "Infinity", "arguments", "eval"]);

return {
"VariableDeclaration, :function, CatchClause"(node) {
for (const variable of context.getDeclaredVariables(node)) {
if (variable.defs.length > 0 && RESTRICTED.includes(variable.name)) {
if (variable.defs.length > 0 && RESTRICTED.has(variable.name) && !safelyShadowsUndefined(variable)) {
context.report({
node: variable.defs[0].name,
message: "Shadowing of global property '{{idName}}'.",
Expand Down
16 changes: 14 additions & 2 deletions tests/lib/rules/no-shadow-restricted-names.js
Expand Up @@ -24,6 +24,13 @@ ruleTester.run("no-shadow-restricted-names", rule, {
{
code: "try {} catch {}",
parserOptions: { ecmaVersion: 2019 }
},
"var undefined;",
"var undefined; doSomething(undefined);",
"var undefined; var undefined;",
{
code: "let undefined",
parserOptions: { ecmaVersion: 2015 }
}
],
invalid: [
Expand All @@ -39,13 +46,12 @@ ruleTester.run("no-shadow-restricted-names", rule, {
]
},
{
code: "function undefined(undefined) { var undefined; !function undefined(undefined) { try {} catch(undefined) {} }; }",
code: "function undefined(undefined) { !function undefined(undefined) { try {} catch(undefined) {} }; }",
errors: [
{ message: "Shadowing of global property 'undefined'.", type: "Identifier" },
{ message: "Shadowing of global property 'undefined'.", type: "Identifier" },
{ message: "Shadowing of global property 'undefined'.", type: "Identifier" },
{ message: "Shadowing of global property 'undefined'.", type: "Identifier" },
{ message: "Shadowing of global property 'undefined'.", type: "Identifier" },
{ message: "Shadowing of global property 'undefined'.", type: "Identifier" }
]
},
Expand Down Expand Up @@ -110,6 +116,12 @@ ruleTester.run("no-shadow-restricted-names", rule, {
{ message: "Shadowing of global property 'undefined'.", type: "Identifier" },
{ message: "Shadowing of global property 'undefined'.", type: "Identifier" }
]
},
{
code: "var undefined; undefined = 5;",
errors: [
{ message: "Shadowing of global property 'undefined'.", type: "Identifier" }
]
}
]
});

0 comments on commit 533d240

Please sign in to comment.