Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 477222

Summary: [quickfix] Offer a Quick Fix to replace a "new Array()" statement
Product: [ECD] Orion Reporter: Steve Northover <steve_northover>
Component: JS ToolsAssignee: Michael Rennie <Michael_Rennie>
Status: RESOLVED FIXED QA Contact:
Severity: enhancement    
Priority: P3 CC: mamacdon, Michael_Rennie, Silenio_Quarti
Version: unspecified   
Target Milestone: 10.0   
Hardware: PC   
OS: Windows 7   
Whiteboard:

Description Steve Northover CLA 2015-09-11 11:18:15 EDT
1) Type in the following code:

	var digits = new Array (3);

2) You will get a warning about "new Array (3)"

What should the code be for this case?  There is confusion around "new Array".

new Array(3) - create an array with 3 undefined elements
new Array(3 2) - create with the elements [3, 2]

Since indexing in JavaScript is loose, the "new Array(3)" is not really needed, however, it captures the intent in the code that the array should be of size 3 and allows for-loops to run on it over the length.  What is the "correct code" here?
Comment 1 Michael Rennie CLA 2015-09-14 11:51:28 EDT
(In reply to Steve Northover from comment #0)
> 1) Type in the following code:
> 
> 	var digits = new Array (3);
> 
> 2) You will get a warning about "new Array (3)"
> 
> What should the code be for this case?  There is confusion around "new
> Array".
> 
> new Array(3) - create an array with 3 undefined elements
> new Array(3 2) - create with the elements [3, 2]
> 
> Since indexing in JavaScript is loose, the "new Array(3)" is not really
> needed, however, it captures the intent in the code that the array should be
> of size 3 and allows for-loops to run on it over the length.  What is the
> "correct code" here?

The "correct" code here is to always use array literal notation:

var digits = [];

You gain nothing from using the constructor, and can potentially introduce bugs using it.
Comment 2 Mark Macdonald CLA 2015-09-14 12:03:47 EDT
(In reply to Michael Rennie from comment #1)
> You gain nothing from using the constructor, and can potentially introduce
> bugs using it.

That is true, but the fix doesn't have the same semantics as the code the user typed. The same code written properly would be either:

> // Fix for `new Array(3)`
> var digits = [];
> for (var i = 0; i < 3; i++) {
>   digits[i] = undefined;
> }

Or in Steve's second case:

> // Fix for `new Array(3, 2)`
> var digits = [3, 2];

IMO if the fix actually re-wrote your bad code explicitly, it would make the behavior of new Array() clear to anyone who doesn't understand it, and then they could fix the resulting code if it didn't match their intent.
Comment 3 Silenio Quarti CLA 2015-09-14 12:11:27 EDT
var digits = n[];

is not the same as:

var digits = new Array (3);


The length of "digits" is 3 in the later case. There are situations when having an array of a given length with uninitialized values is valid.  What is the proper way to achieve that? Mark's code in comment#2?


Take a look at the documentation [1] and search for "arrayLength".

[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
Comment 4 Silenio Quarti CLA 2015-09-14 12:15:13 EDT
The for loop would be very inefficient for large arrays.  Another option is:

var digits = [];
digits.length = 3;

But really that is not any better than the constructor call.
Comment 5 Mark Macdonald CLA 2015-09-14 12:25:39 EDT
(In reply to Silenio Quarti from comment #4)

Or in your case, the "I know what I'm doing" option

> /*eslint-disable no-new-array*/
Comment 6 Michael Rennie CLA 2015-09-14 13:11:21 EDT
(In reply to Mark Macdonald from comment #2)
 
> That is true, but the fix doesn't have the same semantics as the code the
> user typed. The same code written properly would be either:
> 

Right, my sugestion for using array literal notation wasn't so much how steve should fix his code, rather the use ingeneral. 

Case one could be simplified as (for smaller arrays)

var digits = [undefined, undefined, undefined];
 
> IMO if the fix actually re-wrote your bad code explicitly, it would make the
> behavior of new Array() clear to anyone who doesn't understand it, and then
> they could fix the resulting code if it didn't match their intent.

This should be easy, a simple static analysis of the constructor params and creating the matching loops (if needed). We do something related to this for correcting sparse arrays.

> There are situations when having an array of a given length with uninitialized values is valid.

Personally I would disagree, I can't think of one useful case for creating an array and filling it with undefined (or null for sparse arrays). Can you give me a real world example where this is beneficial compared to simply creating an empty array?
Comment 7 Michael Rennie CLA 2015-09-14 13:12:52 EDT
(In reply to Mark Macdonald from comment #5)
> (In reply to Silenio Quarti from comment #4)
> 
> Or in your case, the "I know what I'm doing" option
> 
> > /*eslint-disable no-new-array*/

Its been on my backburner for a while to create a quick-fix for every problem that allows a user to ignore the problems from a given rule (in-file). But my fear was that devs would start using it to 'remove' the problems from their source without actually fixing them :)
Comment 8 Mark Macdonald CLA 2015-09-14 13:34:23 EDT
(In reply to Michael Rennie from comment #7)

> Can you give me a real world example where this is beneficial compared to 
> simply creating an empty array?

Try this -- http://jsperf.com/new-array-vs-literal-1000-elements

Setting the initial capacity with `new Array(n)` or `array.length = n` does seem to make a difference when filling a large array. For code like textView.js or textModel.js, when we do operations like this

> new Array(lineCount)

The difference could be significant when lineCount is very large.
Comment 9 Michael Rennie CLA 2015-09-14 13:44:45 EDT
(In reply to Mark Macdonald from comment #8)
> (In reply to Michael Rennie from comment #7)
> 
> > Can you give me a real world example where this is beneficial compared to 
> > simply creating an empty array?
> 
> Try this -- http://jsperf.com/new-array-vs-literal-1000-elements
> 
> Setting the initial capacity with `new Array(n)` or `array.length = n` does
> seem to make a difference when filling a large array. For code like
> textView.js or textModel.js, when we do operations like this
> 
> > new Array(lineCount)
> 
> The difference could be significant when lineCount is very large.

Makes sense, thanks for pointing that out. I didn't know we used arrays like that in our text stuff.
Comment 10 Steve Northover CLA 2015-09-14 14:34:59 EDT
Bottom line?  "Array (n) can make sense?"
Comment 11 Michael Rennie CLA 2015-09-14 16:14:43 EDT
(In reply to Steve Northover from comment #10)
> Bottom line?  "Array (n) can make sense?"

Yeah, for really large arrays the jsperf is saying it performs better. I think Mark's suggestion for the fix would make the most sense. Then devs can see what the code looks like and as he noted, maybe change what they were wanting to do.
Comment 12 Steve Northover CLA 2015-09-14 16:19:32 EDT
Can we say that "new Array(n)" is a good pattern while things like "new Array(n, m) etc." is bad?  I guess that would be redefining what eslint says and that would be a bad thing.

We don't want to disable all new-array's though for every place in the file.
Comment 13 Michael Rennie CLA 2015-09-14 16:54:18 EDT
(In reply to Steve Northover from comment #12)
> Can we say that "new Array(n)" is a good pattern while things like "new
> Array(n, m) etc." is bad?  I guess that would be redefining what eslint says
> and that would be a bad thing.
> 
> We don't want to disable all new-array's though for every place in the file.

Looks like to once again match upstream ESLint, we should not even be marking the single param case:

http://eslint.org/docs/rules/no-array-constructor.html
Comment 14 Steve Northover CLA 2015-09-14 17:21:17 EDT
Do we just need to update eslint to a better version?
Comment 15 Michael Rennie CLA 2015-09-14 18:03:21 EDT
(In reply to Steve Northover from comment #14)
> Do we just need to update eslint to a better version?

In this case, no, we have to update the code in our version of the rule.
Comment 16 Michael Rennie CLA 2015-09-21 23:00:21 EDT
(In reply to Michael Rennie from comment #15)
> (In reply to Steve Northover from comment #14)
> > Do we just need to update eslint to a better version?
> 
> In this case, no, we have to update the code in our version of the rule.

Updated our rule to match the upstream rule (plus tests):

http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=efef883af58deb9e20af6857b69bee51535bc3a3
Comment 17 Michael Rennie CLA 2015-09-21 23:35:58 EDT
Added quick fix (plus tests) to turn new expression and call expression arrays into literal notation.

For example:

var ar = new Array('a');        -> var ar = ['a'];
var ar = new Array(1, 2, 3);    -> var ar = [1, 2, 3];
var ar = new Array(1, 'd', {}); -> var ar = [1, 'd', {}]; 
var ar = Array('a');            -> var ar = ['a'];
var ar = Array(1, 2, 3);        -> var ar = [1, 2, 3];
var ar = Array(1, 'd', {});     -> var ar = [1, 'd', {}];
Comment 18 Michael Rennie CLA 2015-09-21 23:36:25 EDT
(In reply to Michael Rennie from comment #17)
> Added quick fix (plus tests) to turn new expression and call expression
> arrays into literal notation.
> 
The commit:

http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=3530b74cd0c9487f63590c29b9af30e05da6a2d6