Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rule idea: no-multi-assign #6424

Closed
silverwind opened this issue Jun 15, 2016 · 30 comments · Fixed by singapore/lint-condo#218 or renovatebot/renovate#63 · May be fixed by iamhunter/teammates#4
Closed

Rule idea: no-multi-assign #6424

silverwind opened this issue Jun 15, 2016 · 30 comments · Fixed by singapore/lint-condo#218 or renovatebot/renovate#63 · May be fixed by iamhunter/teammates#4
Assignees
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion archived due to age This issue has been archived; please open a new issue for any further discussion feature This change adds a new feature to ESLint help wanted The team would welcome a contribution from the community for this issue rule Relates to ESLint's core rules

Comments

@silverwind
Copy link
Contributor

silverwind commented Jun 15, 2016

Proposing a new rule which forbids multiple assignments in a single expression.

Code that should trigger a warning:

a = b = c;

Code that should not trigger a warning:

b = c;
a = c;

They're hard to read and it's not immediately clear what is assigned to what. In above example, one could think that the order of evaluation is left-to-right, while it actually is right-to-left.

@eslintbot eslintbot added the triage An ESLint team member will look at this issue soon label Jun 15, 2016
@ilyavolodin ilyavolodin added rule Relates to ESLint's core rules feature This change adds a new feature to ESLint evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion and removed triage An ESLint team member will look at this issue soon labels Jun 16, 2016
@ilyavolodin
Copy link
Member

Hmm... It's a valid rule, but I'm not 100% sure it's important enough to be included in the core. We are trying to limit new rules for core to "must-haves", I'm not sure it meets the bar. @eslint/eslint-team thoughts?

@mikesherov
Copy link
Contributor

I really like this as a core rule. 👍

@michaelficarra
Copy link
Member

michaelficarra commented Jun 16, 2016

@silverwind Evaluation order is very much left-to-right in assignment.

z = x = {};
y = {};
x.a = x = y;
x === y; // true
z.a === y; // true

@silverwind
Copy link
Contributor Author

@michaelficarra maybe I was unclear, but it definitely assigns the right side first:

o = {};
Object.defineProperty(o, "a", {set: function() { console.log("set a"); } });
Object.defineProperty(o, "b", {set: function() { console.log("set b"); } });
o.a = o.b = 1
// prints:
// set b
// set a

@mysticatea
Copy link
Member

mysticatea commented Jun 18, 2016

Maybe x.a = x = y; evaluates as the following flow.

// x.a = x = y;

// save references of destinations by left-to-right.
d1 = &x.a
d2 = &x

// assign the value to the references by right-to-left.
*d2 = y;
*d1 = y;

I guess that @michaelficarra's concern is that a = b = c and b = c; a = c; are not always same.

@mysticatea
Copy link
Member

let x = {}
let obj = {
    get x() {
        console.log("get obj.x")
        return this._x
    },
    set x(value) {
        console.log("set obj.x", value)
        this._x = value
    },

    get y() {
        console.log("get obj.y")
        return this._y
    },
    set y(value) {
        console.log("set obj.y", value)
        this._y = value
    },

    _x: x,
    _y: x
}

obj.x.x = obj.y.y = obj.x = obj.y = 1

console.log(x)
get obj.x
get obj.y
set obj.y 1
set obj.x 1
{ y: 1, x: 1 }

@SpadeAceman
Copy link

Wow @mysticatea – that example hurts my brain!

I've been using my own custom-written version of this rule since I saw it suggested here. It's helped me identify and clean up some confusing code I'd written a while back, so to me it seems like a good core rule to have. (Using multiple assignments can seem like a good implementation of DRY, but IMO what it gains in being concise it loses in being less than clear down the road.)

@platinumazure
Copy link
Member

This seems simple enough to implement and could be useful (as a stylistic rule).

One thing that would probably help rustle up some 👍s from the team is if you can find a prevalent style guide or a JSCS rule which disallows multiple assignments.

@nzakas
Copy link
Member

nzakas commented Aug 3, 2016

Unless someone steps forward to champion this rule, it's time to close this issue.

@silverwind
Copy link
Contributor Author

There's another unmentioned risk of multiple assignment, which is the creation of unintended globals, like b here:

let a = b = 2;

But I've just tried it and no-undef correctly identifies the issue, so that somewhat devalues this possible rule in my eyes.

@nzakas Let's keep this issue open a few more days, and close it off as rejected if no strong case can be presented.

@mysticatea
Copy link
Member

Actually, I don't oppose this rule. Though I like a rule which has always/never options since I think this is a stylistic rule.

If someone champions this rule, I'll support.

@SpadeAceman
Copy link

SpadeAceman commented Aug 8, 2016

An example of an issue only this rule could catch:

a += b += 2 // the second += is most likely a mistake

a += b + 2 // this is likely what was intended

As for precedent, aside from Python, a number of recently-created languages also treat assignments as statements, or expressions which return no value:

While I wasn't able to find more info on why these these language design decisions were made, it seems significant to me that they all reject the concept of treating assignments as expressions.

@michaelficarra
Copy link
Member

@mysticatea What would an always option for this rule even mean?

@SpadeAceman Thanks for the perspective. Redefining this rule as "allow assignments only in statement position" makes it much more appealing. 👍

@platinumazure
Copy link
Member

@michaelficarra Does "statement position" also include loop initialization/afterthought and conditional tests, in your description? We would want to be very careful not to ruin for loops, for example.

@mysticatea
Copy link
Member

@michaelficarra

never

// ✔ GOOD
a = 1
b = 1

// ✘ BAD
a = b = 1

always

// ✔ GOOD
a = b = 1

// ✘ BAD
a = 1
b = 1

Ah, I had not considered about +=.
The rule to prevent a += b = c sounds good to me.

@michaelficarra
Copy link
Member

@platinumazure Anywhere the value is thrown away. So even void (a = b) is technically fine by me, and for(a = b;;) should be allowed, though I don't see why anyone would write that.

@mysticatea Ah, so always means adjacent assignments to the same primitive value or reference? I don't think that's needed.

@mysticatea
Copy link
Member

@michaelficarra OK.

So, I guess this is no-multi-assign which forbids (node.type === "AssignmentExpression" && node.parent.type === "AssignmentExpression").

@platinumazure
Copy link
Member

@mysticatea What about AssignmentExpression with parent of VariableDeclarator? That would look like a "multiple assignment" too, IMO.

@ljharb
Copy link
Sponsor Contributor

ljharb commented Aug 18, 2016

VariableDeclarators are asked about in #6932

@mysticatea
Copy link
Member

@platinumazure Sounds good. (node.type === "AssignmentExpression" && /^(?:Assignment(?:Expression|Pattern)|VariableDeclarator)$/.test(node.parent.type))

@alberto
Copy link
Member

alberto commented Aug 26, 2016

@eslint/eslint-team last call for a champion or else we should close it

@platinumazure
Copy link
Member

I'll champion.

@nzakas
Copy link
Member

nzakas commented Aug 31, 2016

@platinumazure you need to drum up two more 👍s.

@platinumazure
Copy link
Member

platinumazure commented Aug 31, 2016

Maybe just one? I'm counting @mysticatea and @michaelficarra.

@mysticatea, @michaelficarra Are you endorsing this?

@nzakas
Copy link
Member

nzakas commented Sep 1, 2016

I can't count (I think we may need to standardize where we 👍 to make this easier). It also looks like there was a 👍 from @mikesherov and @IanVS, so marking as accepted.

@nzakas nzakas added accepted There is consensus among the team that this change meets the criteria for inclusion and removed evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion labels Sep 1, 2016
@kaicataldo kaicataldo added the help wanted The team would welcome a contribution from the community for this issue label Nov 4, 2016
@kaicataldo
Copy link
Member

@platinumazure Are you still championing this?

@platinumazure
Copy link
Member

platinumazure commented Jan 8, 2017 via email

@kaicataldo
Copy link
Member

I'm up for keeping it open! Just wanted to make sure it still had a champion :) Thanks!

stewx pushed a commit to stewx/eslint that referenced this issue Jan 11, 2017
@stewx
Copy link
Contributor

stewx commented Jan 11, 2017

I will be submitting a PR for this shortly.

stewx pushed a commit to stewx/eslint that referenced this issue Jan 11, 2017
stewx pushed a commit to stewx/eslint that referenced this issue Jan 11, 2017
stewx added a commit to stewx/eslint that referenced this issue Jan 11, 2017
@stewx
Copy link
Contributor

stewx commented Jan 11, 2017

PR here: #7904

stewx pushed a commit to stewx/eslint that referenced this issue Jan 12, 2017
stewx added a commit to stewx/eslint that referenced this issue Jan 12, 2017
stewx added a commit to stewx/eslint that referenced this issue Jan 17, 2017
stewx added a commit to stewx/eslint that referenced this issue Jan 17, 2017
stewx added a commit to stewx/eslint that referenced this issue Jan 19, 2017
stewx added a commit to stewx/eslint that referenced this issue Jan 20, 2017
@eslint-deprecated eslint-deprecated bot locked and limited conversation to collaborators Feb 6, 2018
@eslint-deprecated eslint-deprecated bot added the archived due to age This issue has been archived; please open a new issue for any further discussion label Feb 6, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion archived due to age This issue has been archived; please open a new issue for any further discussion feature This change adds a new feature to ESLint help wanted The team would welcome a contribution from the community for this issue rule Relates to ESLint's core rules
Projects
None yet