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

Do not autofix self-referential computed properties in require-computed-macros rule #1121

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

runspired
Copy link

partially resolves #1118 (we should still add a new rule)

@runspired runspired changed the title fix: require-computed-macros should not autofix self-referential computers fix: require-computed-macros should not autofix self-referential computeds Mar 29, 2021
@rwjblue rwjblue requested a review from bmish March 29, 2021 21:13
@bmish bmish added the Bug label Apr 1, 2021
@bmish bmish changed the title fix: require-computed-macros should not autofix self-referential computeds Do not autofix self-referential computed properties in require-computed-macros rule Apr 5, 2021
const isNotFixable = detectCircularKey(nodeComputedProperty, [text]);

if (isNotFixable) {
context.report({
Copy link
Member

Choose a reason for hiding this comment

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

Instead of duplicating the context.report call, we should actually just return null inside the fixer function if we encounter a non-fixable situation. That goes for all these reporting functions and it will simplify all this code a lot.

fix(fixer) {
  if (!isFixable(...)) { return null; }
  ...  
}

Copy link
Author

Choose a reason for hiding this comment

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

@bmish I did that initially but it is linted against, I also suspect it's not good practice since it might cause eslint to report the violation as fixable when it is not.

Copy link
Member

@bmish bmish Apr 5, 2021

Choose a reason for hiding this comment

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

It's not linted against, we actually have some examples of autofixers with return null in our codebase. And it's not bad practice, in fact it's common practice to have autofixers which ignore certain situations, and this is how we do it. I can assure you it will result in much simpler code :)

Copy link
Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Using return null won't trigger that rule. return null indicates that you don't want to autofix in a particular situation.

{
code: 'class Test { @computed() get foo() { return this.foo; } }',
options: [{ includeNativeGetters: true }],
output: 'class Test { @computed() get foo() { return this.foo; } }',
Copy link
Member

Choose a reason for hiding this comment

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

We should use output: null to indicate there's no autofix, it's more clear and concise.

Copy link
Author

Choose a reason for hiding this comment

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

wouldn't that imply the rule was deleting code?

Copy link
Member

Choose a reason for hiding this comment

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

No. It means there's no autofix.

And there's actually a lint rule to enforce this but unfortunately it's not working on this code because the array of test cases has .map(addComputedImport) on it.

https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/blob/master/docs/rules/prefer-output-null.md

@@ -287,5 +287,56 @@ ruleTester.run('require-computed-macros', rule, {
output: "class Test { @computed.mapBy('children', 'age') someProp }",
errors: [{ message: ERROR_MESSAGE_MAP_BY, type: 'MethodDefinition' }],
},

// Self Referential
Copy link
Member

Choose a reason for hiding this comment

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

I think we're adding a few redundant test cases as well as missing some test cases.

We need the following test cases basic test cases which test a simple single-arg return expression that will be converted into the reads macro:

  • class Test { @computed() get foo() { return this.foo; } }
  • class Test { foo = computed(function() { return this.foo; }) } // non-decorator style
  • import EmberObject from '@ember/object'; EmberObject.extend({ foo: computed(function() { return this.foo; }) } // classic class

And we need to test other types of expressions that will be converted into other macros:

  • class Test { @computed() get a() { return this.a && this.b; } } // logical
    *class Test { @computed() get a() { return this.a > this.b; } } // binary
    *import EmberObject from '@ember/object'; EmberObject.extend({ computed(function() { return this.chores.filterBy('done', true); }) // function

We don't need to test anything related to the dependent key arguments (computed('this-dependent-key-does-not-matter', function () {})) since those end up ignored anyway.

Copy link
Author

Choose a reason for hiding this comment

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

The redundancy is intentional, it covers both forms of classic and both forms of class based declarations both with and without args being supplied to the computed as dependent keys (since I encountered errors from conversion from both in the wild).

Can add tests for a few of the others.

Copy link
Member

@bmish bmish Apr 5, 2021

Choose a reason for hiding this comment

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

Sounds good. A comment next to each test case explaining what variation it is testing would help.

@@ -115,6 +115,17 @@ function getThisExpressions(nodeLogicalExpression) {
return arrayOfThisExpressions.reverse();
}

function detectCircularKey(node, macroArgs) {
if (node.parent && node.parent.key) {
Copy link
Member

Choose a reason for hiding this comment

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

There should always be a parent, but we do need to check what node type the parent is before we check its key. That way, we won't end up using the key of some unexpected kind of node.

Copy link
Author

Choose a reason for hiding this comment

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

oddly I believe I encountered a no parent situation in our own test suite. Will double check that.

Copy link
Member

Choose a reason for hiding this comment

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

ESLint adds the parent when it runs the rule test cases. But if you write separate unit tests for this function by itself, then it won't have a parent. So feel free to add the parent guard but you only need if you write separate unit tests for the function itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] require-computed-macros can autofix to self-referential macros
2 participants