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

Update: add fixer for arrow-body-style #7240

Merged
merged 4 commits into from Oct 18, 2016

Conversation

not-an-aardvark
Copy link
Member

@not-an-aardvark not-an-aardvark commented Sep 25, 2016

What is the purpose of this pull request? (put an "X" next to item)

[ ] Documentation update
[ ] Bug fix (template)
[ ] New rule (template)
[ ] Changes an existing rule (template)
[x] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

Please check each item to ensure your pull request is ready:

  • I've read the pull request guide
  • I've included tests for my change
  • I've updated documentation for my change (if appropriate)

What changes did you make? (Give an overview)

This adds a fixer for arrow-body-style.

/* eslint arrow-body-style: [2, "as-needed"] */

var foo = () => { return 5; };
var bar = () => { return {ok: true}; };

// gets fixed to:

var foo = () =>   5 ;
var bar = () =>   ({ok: true}) ;
/* eslint arrow-body-style: [2, "always"] */

var foo = () => 5;
var bar = () => ({ok: true});

// gets fixed to:

var foo = () => {return 5};
var bar = () => {return ({ok: true})};
/* eslint arrow-body-style: [2, "never"] */

var foo = () => { bar(); return baz(); }; // not fixed

The fixer preserves all comments and whitespace. On the next pass, the fixers for brace-style, indent, no-multi-spaces, no-extra-parens, semi, and keyword-spacing will be able to clean up stylistic issues from the outputted code.

Is there anything you'd like reviewers to focus on?

We should make sure there are no cases where replacing a block body with a literal (or vice versa) causes a SyntaxError.

The build will fail at the moment due to #7239. I'll rebase onto master after that's fixed.

@mention-bot
Copy link

@not-an-aardvark, thanks for your PR! By analyzing the annotation information on this pull request, we identified @ajhyndman, @alberto and @vitorbal to be potential reviewers

@eslintbot
Copy link

LGTM

@vitorbal vitorbal added enhancement This change enhances an existing feature of ESLint rule Relates to ESLint's core rules evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion labels Sep 26, 2016
@not-an-aardvark
Copy link
Member Author

I'll champion this.

@not-an-aardvark not-an-aardvark self-assigned this Sep 30, 2016
@nzakas
Copy link
Member

nzakas commented Sep 30, 2016

@not-an-aardvark championing also means you need to drum up support for this amongst the team, gathering the necessary 👍s. (Just don't want this to sit around ignored)

@mysticatea
Copy link
Member

mysticatea commented Oct 1, 2016

We have received many questions "how to fix this arrow-body-style error?".
Also, #5498 still has top traffic in all issues.

So I think the autofix of arrow-body-style would help users great.

@mysticatea mysticatea 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 Oct 1, 2016
}

if (asNeeded && requireReturnForObjectLiteral && blockBody[0].type === "ReturnStatement" &&
blockBody[0].argument.type === "ObjectExpression") {
Copy link
Member

@mysticatea mysticatea Oct 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReturnStatement#argument property needs null check.
Chould you add tests like a => { return }?

const sourceText = sourceCode.getText();
const textBeforeReturn = sourceText.slice(arrowBody.range[0] + 1, blockBody[0].range[0]);
const textBetweenReturnAndValue = sourceText.slice(sourceCode.getFirstToken(blockBody[0]).range[1], returnValue.range[0]);
const returnValueText = returnValue.type === "ObjectExpression" ? `(${sourceCode.getText(returnValue)})` : sourceCode.getText(returnValue);
Copy link
Member

@mysticatea mysticatea Oct 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can cause a syntax error after autofix. It needs parentheses if the first token of returnValue is {.

key => {
    return {a: 1, b: 2}[key]
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, it seems wrong if the returnValue is parenthesized.

() => {
    return ({a: 1, b: 2})
}

// ↓ fixed

() => 
     (({a: 1, b: 2})


return fixer.replaceTextRange(
[firstBodyToken.range[0], node.range[1]],
`{return ${sourceCode.getText().slice(firstBodyToken.range[0], node.range[1])}}`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my ideal, it removes parentheses which are enclosing the body if the first token of the body is { because it's restricted by syntax.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought of doing that, but that could cause comments to get removed:

var foo = () => ( /* comment */ {ok: true});

// gets fixed to:

var foo = () => {return ( /* comment */ {ok: true});};

I think users can use no-extra-parens if they want to avoid the unnecessary parens.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough.
Thank you!

@eslintbot
Copy link

LGTM

@not-an-aardvark
Copy link
Member Author

I think the new commit addresses all of the issues.

@eslintbot
Copy link

LGTM

@mysticatea
Copy link
Member

Great!

Copy link
Member

@mysticatea mysticatea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you!

Copy link
Member

@mysticatea mysticatea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found a problem now

let a = () => {
    return 1
}
(() => doSomething())()

Copy link
Member

@mysticatea mysticatea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind...
In this case, originate code has a problem...

@not-an-aardvark
Copy link
Member Author

not-an-aardvark commented Oct 2, 2016

I think that is actually a problem with this fixer:

var x = () => { return 1 } // <-- ASI inserts a semicolon here, x will always return 1
(2) // <-- This is an ExpressionStatement with the literal 2

// ↓ fixed

var x = () =>   1  // <-- ASI does not insert a semicolon here, x will throw a TypeError when called
(2)  // <-- This is an argument of calling 1 as a function

So I suppose we should avoid fixes if the next line begins with (, [, or ```, or if the returned value ends with ++ or `--`.

edit: Actually, we don't need to account for ++ and --, because if x++ or x-- are used in a ReturnExpression then ++ has to be on the same line as x.

@mysticatea
Copy link
Member

My brain had been confused since it was right after waking. 😅

Copy link
Member

@mysticatea mysticatea left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is the ASI problem.

@platinumazure
Copy link
Member

A thought: Should we just always wrap the value in parentheses, and then let no-extra-parens decide if the parentheses can be removed?

@not-an-aardvark
Copy link
Member Author

@platinumazure Could you elaborate on why always wrapping the value in parentheses would be beneficial? I don't understand why we would want to do that.

@platinumazure
Copy link
Member

Hmm. Originally it was for ASI reasons, but I'm realizing that it doesn't fix all such situations.

Useful fix:

() => { return x++ } // becomes: () => (x++), avoids ASI problems on next line

Less useful:

() => { return x } // becomes () => (x)\n(1), does not solve ASI problem
(1)

So my suggestion wasn't as good as I thought.

@not-an-aardvark
Copy link
Member Author

I don't think () => { return x++ } is an ASI problem anyway:

() => { return x++ }
foo

// ↓ fixed

() =>   x++ // this still increases x and not foo, because the ++ is on the same line as x.
foo

@eslintbot
Copy link

LGTM

@eslintbot
Copy link

LGTM

@eslintbot
Copy link

LGTM

@@ -72,6 +84,7 @@ ruleTester.run("arrow-body-style", rule, {
},
{
code: "var foo = () => { return bar(); };",
output: "var foo = () => bar() ;",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we strip the extra spaces before bar()? It seems like you'd expect just one space because that's what is between return and bar().

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two extra spaces are from after the arrow, and after the opening curly. At the moment, the fixer keeps all text between the tokens in case it contains comments, newlines, etc. I think people can use no-multi-spaces and semi-spacing if they don't want the extra spaces.

Would you recommend that we special-case the fixes where there are only space characters between these tokens?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think we have to special case this. I can't see anyone being happy with more than one space after =>.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to add a special case for this.

@eslintbot
Copy link

LGTM

Copy link
Member

@nzakas nzakas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks.

@nzakas nzakas merged commit e064a25 into eslint:master Oct 18, 2016
@not-an-aardvark not-an-aardvark deleted the arrow-body-style-fixer branch October 18, 2016 18:11
@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 enhancement This change enhances an existing feature of ESLint rule Relates to ESLint's core rules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

9 participants