douglascrockford/JSLint — pull request #164 by jacksonrayhamilton (2014-10-26 05:07:16+00:00)
Title: Fix bug where setting "assume" options to false via directive did nothing.

Take for instance

```javascript
JSLINT([
'/*jslint evil: true */',
'eval("1+1");'
].join('\n'), {
evil: false
});
console.log(JSON.stringify(JSLINT.data().errors, null, ' '));
```

which produces

```javascript
[]
```

And take for instance

```javascript
JSLINT([
'/*jslint evil: false */',
'eval("1+1");'
].join('\n'), {
evil: true
});
console.log(JSON.stringify(JSLINT.data().errors, null, ' '));
```

which produces

```javascript
[
{
"id": "(error)",
"raw": "eval is evil.",
"code": "evil",
"evidence": "eval(\"1+1\");",
"line": 2,
"character": 1,
"a": "eval",
"reason": "eval is evil."
}
]
```

The demonstrated behavior seems to imply that the `/*jslint` directive overrides
options set in the second parameter to `JSLINT`. However, this is not always the
case.

Take for instance

```javascript
JSLINT([
'/*jslint browser: true */',
'setTimeout();'
].join('\n'), {
browser: false
});
console.log(JSON.stringify(JSLINT.data().errors, null, ' '));
```

which produces

```javascript
[]
```

Then take for instance

```javascript
JSLINT([
'/*jslint browser: false */',
'setTimeout();'
].join('\n'), {
browser: true
});
console.log(JSON.stringify(JSLINT.data().errors, null, ' '));
```

which, unexpectedly, produces...

```javascript
[]
```

`browser` has been set to `false`, so I would expect this output:

```javascript
[
{
"id": "(error)",
"raw": "'{a}' was used before it was defined.",
"code": "used_before_a",
"evidence": "setTimeout();",
"line": 2,
"character": 1,
"a": "setTimeout",
"reason": "'setTimeout' was used before it was defined."
}
]
```

The problem appears to stem from the way "assume" options populate JSLint's
global namespace.

After examining the `assume()` function I came to the conclusion that assuming
"assumed" predefineds is a one-time, one-way operation. My solution makes
assumptions retractable, but without accidentally unsetting intersecting
assumptions.

With my fix, the above "expected" output would be produced instead.

In an effort to convince you of the value of my fix: Additionally, this code

```javascript
JSLINT([
'/*jslint node: false */',
'setTimeout();'
].join('\n'), {
browser: true,
node: true
});
console.log(JSON.stringify(JSLINT.data().errors, null, ' '));
```

would continue to produce

```javascript
[]
```

Even though `browser` and `node` each define the `setTimeout` global, and even
though `node: false` is specified, the intersection between the browser's and
node's namespaces is honored.

The following, then, would be necessary to expunge `setTimeout` from JSLint's
global namespace:

```javascript
JSLINT([
'/*jslint browser: false, node: false */',
'setTimeout();'
].join('\n'), {
browser: true,
node: true
});
console.log(JSON.stringify(JSLINT.data().errors, null, ' '));
```

Also good: After my fix, this

```javascript
JSLINT([
'/*jslint browser: false */',
'setTimeout();'
].join('\n'), {
browser: true,
predef: [
'setTimeout'
]
});
console.log(JSON.stringify(JSLINT.data().errors, null, ' '));
```

still produces

```javascript
[]
```

Even if you are unhappy with my solution, I hope you will at least consider that
this behavior may be a bug, and one worth addressing. Thank you for your time.