Skip to content

Commit

Permalink
New option: impliedStrict
Browse files Browse the repository at this point in the history
  • Loading branch information
nre committed Jan 28, 2016
1 parent 597ecb4 commit 17f5688
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ function defaultOptions() {
optimistic: false,
directive: false,
nodejsScope: false,
impliedStrict: false,
sourceType: 'script', // one of ['script', 'module']
ecmaVersion: 5
};
Expand Down Expand Up @@ -103,6 +104,8 @@ function updateDeeply(target, override) {
* @param {boolean} [providedOptions.nodejsScope=false]- whether the whole
* script is executed under node.js environment. When enabled, escope adds
* a function scope immediately following the global scope.
* @param {boolean} [providedOptions.impliedStrict=false]- implied strict mode
* (if ecmaVersion >= 5).
* @param {string} [providedOptions.sourceType='script']- the source type of the script. one of 'script' and 'module'
* @param {number} [providedOptions.ecmaVersion=5]- which ECMAScript version is considered
* @return {ScopeManager}
Expand Down
4 changes: 4 additions & 0 deletions src/referencer.js
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,10 @@ export default class Referencer extends esrecurse.Visitor {
this.scopeManager.__nestModuleScope(node);
}

if (this.scopeManager.isStrictModeSupported() && this.scopeManager.isImpliedStrict()) {
this.currentScope().isStrict = true;
}

this.visitChildren(node);
this.close(node);
}
Expand Down
8 changes: 8 additions & 0 deletions src/scope-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ export default class ScopeManager {
return this.__options.sourceType === 'module';
}

isImpliedStrict() {
return this.__options.impliedStrict;
}

isStrictModeSupported() {
return this.__options.ecmaVersion >= 5;
}

// Returns appropriate scope for this node.
__get(node) {
return this.__nodeToScope.get(node);
Expand Down
125 changes: 125 additions & 0 deletions test/implied-strict.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2015 Yusuke Suzuki <[email protected]>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import { expect } from 'chai';
import { parse } from '../third_party/esprima';
import { analyze } from '..';

describe('impliedStrict option', function() {
it('ensures all user scopes are strict if ecmaVersion >= 5', function() {
const ast = parse(`
function foo() {
function bar() {
'use strict';
}
}
`);

const scopeManager = analyze(ast, {ecmaVersion: 5, impliedStrict: true});
expect(scopeManager.scopes).to.have.length(3);

let scope = scopeManager.scopes[0];
expect(scope.type).to.be.equal('global');
expect(scope.block.type).to.be.equal('Program');
expect(scope.isStrict).to.be.true;

scope = scopeManager.scopes[1];
expect(scope.type).to.be.equal('function');
expect(scope.block.type).to.be.equal('FunctionDeclaration');
expect(scope.isStrict).to.be.true;

scope = scopeManager.scopes[2];
expect(scope.type).to.be.equal('function');
expect(scope.block.type).to.be.equal('FunctionDeclaration');
expect(scope.isStrict).to.be.true;
});

it('ensures impliedStrict option is only effective when ecmaVersion option >= 5', function() {
const ast = parse(`
function foo() {}
`);

const scopeManager = analyze(ast, {ecmaVersion: 3, impliedStrict: true});
expect(scopeManager.scopes).to.have.length(2);

let scope = scopeManager.scopes[0];
expect(scope.type).to.be.equal('global');
expect(scope.block.type).to.be.equal('Program');
expect(scope.isStrict).to.be.false;

scope = scopeManager.scopes[1];
expect(scope.type).to.be.equal('function');
expect(scope.block.type).to.be.equal('FunctionDeclaration');
expect(scope.isStrict).to.be.false;
});

it('omits a nodejs global scope when ensuring all user scopes are strict', function() {
const ast = parse(`
function foo() {}
`);

let scopeManager = analyze(ast, {ecmaVersion: 5, nodejsScope: true, impliedStrict: true});
expect(scopeManager.scopes).to.have.length(3);

let scope = scopeManager.scopes[0];
expect(scope.type).to.be.equal('global');
expect(scope.block.type).to.be.equal('Program');
expect(scope.isStrict).to.be.false;

scope = scopeManager.scopes[1];
expect(scope.type).to.be.equal('function');
expect(scope.block.type).to.be.equal('Program');
expect(scope.isStrict).to.be.true;

scope = scopeManager.scopes[2];
expect(scope.type).to.be.equal('function');
expect(scope.block.type).to.be.equal('FunctionDeclaration');
expect(scope.isStrict).to.be.true;
});

it('omits a module global scope when ensuring all user scopes are strict', function() {
const ast = parse(`
function foo() {}`,
{sourceType: 'module'}
);

let scopeManager = analyze(ast, {ecmaVersion: 6, impliedStrict: true, sourceType: 'module'});
expect(scopeManager.scopes).to.have.length(3);

let scope = scopeManager.scopes[0];
expect(scope.type).to.be.equal('global');
expect(scope.block.type).to.be.equal('Program');
expect(scope.isStrict).to.be.false;

scope = scopeManager.scopes[1];
expect(scope.type).to.be.equal('module');
expect(scope.isStrict).to.be.true;

scope = scopeManager.scopes[2];
expect(scope.type).to.be.equal('function');
expect(scope.block.type).to.be.equal('FunctionDeclaration');
expect(scope.isStrict).to.be.true;
});
});

// vim: set sw=4 ts=4 et tw=80 :

0 comments on commit 17f5688

Please sign in to comment.