Browse Source

handle assignment patterns

gh-109
Rich-Harris 9 years ago
parent
commit
e13a065bb9
  1. 14
      src/Statement.js
  2. 60
      src/ast/Scope.js
  3. 4
      test/function/assignment-patterns/_config.js
  4. 21
      test/function/assignment-patterns/main.js
  5. 8
      test/function/assignment-patterns/other.js

14
src/Statement.js

@ -3,6 +3,11 @@ import getLocation from './utils/getLocation';
import walk from './ast/walk';
import Scope from './ast/Scope';
const blockDeclarations = {
'const': true,
'let': true
};
function isIife ( node, parent ) {
return parent && parent.type === 'CallExpression' && node === parent.callee;
}
@ -73,7 +78,7 @@ export default class Statement {
switch ( node.type ) {
case 'FunctionDeclaration':
scope.addDeclaration( node.id.name, node, false );
scope.addDeclaration( node, false, false );
case 'BlockStatement':
if ( parent && /Function/.test( parent.type ) ) {
@ -86,7 +91,7 @@ export default class Statement {
// named function expressions - the name is considered
// part of the function's scope
if ( parent.type === 'FunctionExpression' && parent.id ) {
newScope.addDeclaration( parent.id.name, parent, false );
newScope.addDeclaration( parent, false, false );
}
} else {
newScope = new Scope({
@ -108,12 +113,13 @@ export default class Statement {
case 'VariableDeclaration':
node.declarations.forEach( declarator => {
scope.addDeclaration( declarator.id.name, node, true );
const isBlockDeclaration = node.type === 'VariableDeclaration' && blockDeclarations[ node.kind ];
scope.addDeclaration( declarator, isBlockDeclaration, true );
});
break;
case 'ClassDeclaration':
scope.addDeclaration( node.id.name, node, false );
scope.addDeclaration( node, false, false );
break;
}

60
src/ast/Scope.js

@ -1,10 +1,38 @@
import { blank } from '../utils/object';
const blockDeclarations = {
'const': true,
'let': true
const extractors = {
Identifier ( names, param ) {
names.push( param.name );
},
ObjectPattern ( names, param ) {
param.properties.forEach( prop => {
extractors[ prop.key.type ]( names, prop.key );
});
},
ArrayPattern ( names, param ) {
param.elements.forEach( element => {
if ( element ) extractors[ element.type ]( names, element );
});
},
RestElement ( names, param ) {
extractors[ param.argument.type ]( names, param.argument );
},
AssignmentPattern ( names, param ) {
return extractors[ param.left.type ]( names, param.left );
}
};
function extractNames ( param ) {
let names = [];
extractors[ param.type ]( names, param );
return names;
}
export default class Scope {
constructor ( options ) {
options = options || {};
@ -18,26 +46,29 @@ export default class Scope {
if ( options.params ) {
options.params.forEach( param => {
this.declarations[ param.name ] = param;
extractNames( param ).forEach( name => {
this.declarations[ name ] = true;
});
});
}
}
addDeclaration ( name, declaration, isVar ) {
const isBlockDeclaration = declaration.type === 'VariableDeclaration' && blockDeclarations[ declaration.kind ];
addDeclaration ( declaration, isBlockDeclaration, isVar ) {
if ( !isBlockDeclaration && this.isBlockScope ) {
// it's a `var` or function declaration, and this
// it's a `var` or function node, and this
// is a block scope, so we need to go up
this.parent.addDeclaration( name, declaration, isVar );
this.parent.addDeclaration( declaration, isBlockDeclaration, isVar );
} else {
this.declarations[ name ] = declaration;
if ( isVar ) this.varDeclarations.push( name );
extractNames( declaration.id ).forEach( name => {
this.declarations[ name ] = true;
if ( isVar ) this.varDeclarations.push( name );
});
}
}
contains ( name ) {
return !!this.getDeclaration( name );
return this.declarations[ name ] ||
( this.parent ? this.parent.contains( name ) : false );
}
findDefiningScope ( name ) {
@ -51,9 +82,4 @@ export default class Scope {
return null;
}
getDeclaration ( name ) {
return this.declarations[ name ] ||
this.parent && this.parent.getDeclaration( name );
}
}

4
test/function/assignment-patterns/_config.js

@ -0,0 +1,4 @@
module.exports = {
description: 'allows reassigments to default parameters that shadow imports',
babel: true
};

21
test/function/assignment-patterns/main.js

@ -0,0 +1,21 @@
import { bar, baz, x, items, p, q, r, s } from './other';
function foo ( bar = 1, { baz } = { baz: 2 }, [[[,x = 3] = []] = []] = [], ...items ) {
bar += 1;
baz += 1;
x += 1;
let { p, q } = { p: 4, q: 5 };
let [ r, s ] = [ 6, 7 ];
p++;
q += 1;
r = 7;
s = 6;
return bar + baz + x + items.length + p + q + r + s;
}
assert.equal( foo(), 33 );
assert.equal( foo( 2 ), 34 );
assert.equal( foo( 2, { baz: 3 }, [[[99,10]]], 'a', 'b', 'c' ), 45 );

8
test/function/assignment-patterns/other.js

@ -0,0 +1,8 @@
export const bar = 'bar';
export const baz = 'baz';
export const x = 'x';
export const items = 'items';
export const p = 'p';
export const q = 'q';
export const r = 'r';
export const s = 's';
Loading…
Cancel
Save