Skip to content

Add new rule UseEmptyCounterpart #2864

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

Merged
merged 6 commits into from
Jul 31, 2020
Merged

Add new rule UseEmptyCounterpart #2864

merged 6 commits into from
Jul 31, 2020

Conversation

veyndan
Copy link
Contributor

@veyndan veyndan commented Jul 13, 2020

Closes #2850.

* emptySet()
* </compliant>
*/
class UseEmptyCounterpart(config: Config) : Rule(config) {
Copy link
Contributor Author

@veyndan veyndan Jul 13, 2020

Choose a reason for hiding this comment

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

I originally was going to go with "UseEmptyCollection", but decided the name would be too confusing. Even though this rule does apply to "collections" of elements, the name would imply that this rule only applies to objects that implement the Collection interface (which they don't).

@codecov
Copy link

codecov bot commented Jul 13, 2020

Codecov Report

Merging #2864 into master will increase coverage by 0.02%.
The diff coverage is 90.90%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #2864      +/-   ##
============================================
+ Coverage     80.15%   80.18%   +0.02%     
- Complexity     2429     2434       +5     
============================================
  Files           421      422       +1     
  Lines          7384     7405      +21     
  Branches       1354     1358       +4     
============================================
+ Hits           5919     5938      +19     
  Misses          764      764              
- Partials        701      703       +2     
Impacted Files Coverage Δ Complexity Δ
...turbosch/detekt/rules/style/UseEmptyCounterpart.kt 90.00% <90.00%> (ø) 5.00 <5.00> (?)
...rturbosch/detekt/rules/style/StyleGuideProvider.kt 100.00% <100.00%> (ø) 2.00 <0.00> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 539a010...f46b980. Read the comment docs.

Copy link
Member

@schalkms schalkms left a comment

Choose a reason for hiding this comment

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

Thanks for the awesome 1st contribution. The implementation and documentation looks very good.
I have some remarks in regards to the tests. Please have a look at them and contact me, if you have questions.


it("does not report $argInstantiation with arguments") {
val findings = rule.compileAndLint(argInstantiation.argExample)
assertThat(findings).isEmpty()
Copy link
Member

Choose a reason for hiding this comment

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

For future regressions it's a good idea to test some more negative cases.

What happens if the user names a variable or a function setOf? An intensive test suite can prevent errors in the future. Maybe you can come up with more test cases like this.

Copy link
Contributor Author

@veyndan veyndan Jul 13, 2020

Choose a reason for hiding this comment

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

I added your recommended test in 9ba2c35 but have found that it does not pass. This is because I only compare the text of the function instead of the fully qualified name.

I've done some poking around, but can't find the API to retrieve the fully qualified name of the function. Do you have any pointers?

Copy link
Member

Choose a reason for hiding this comment

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

You would need to use type and symbol solving for this. BindingContext is the keyword here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, that quickly put me on the right track.

Fixed in 920ecd0.

Copy link
Member

@schalkms schalkms left a comment

Choose a reason for hiding this comment

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

A merge conflict is still there. Otherwise, this PR looks good from my perspective.

@BraisGabin
Copy link
Member

After #2865 we should move tis rule to the correct module: :detekt-rules-style

Copy link
Member

@cortinico cortinico left a comment

Choose a reason for hiding this comment

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

Thanks for your contribution @veyndan


private val exprsWithEmptyCounterparts = mapOf(
"arrayOf" to "emptyArray",
"listOf" to "emptyList",
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 it should be safe to add also:

    "listOfNotNull" to "emptyList",

Though there is a type restriction (listOfNotNull<Int?> and emptyList<Int> are of different type), but I think it should be safe anyway to flag the usage of a listOfNotNull() with no parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like this idea, though I have an alternative proposal that would flag incorrect usages of listOfNotNull() and listOfNotNull(elem, …).

If we extend the rule UselessCallOnNotNull to also flag invocations of listOfNotNull with all non-nullable arguments, then a no-arg invocation of listOfNotNull would also be flagged. The user can then replace listOfNotNull with listOf for all these incorrect usages. Then UseEmptyCounterpart would flag all the no-arg listOf invocations as a result of this.

WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

If we extend the rule UselessCallOnNotNull to also flag invocations of listOfNotNull with all non-nullable arguments

If possible I'll keep this out of the scope of this PR.

then a no-arg invocation of listOfNotNull would also be flagged. The user can then replace listOfNotNull with listOf for all these incorrect usages. Then UseEmptyCounterpart would flag all the no-arg listOf invocations as a result of this.

Ideally we want to provide immediate feedback with a potential solution. Having the user go through listOfNotNull() -> listOf -> emptyList() and two Detekt runs seems cumbersome to me.

Copy link
Contributor Author

@veyndan veyndan Jul 31, 2020

Choose a reason for hiding this comment

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

Yeah that makes sense. Just to note that when Kotlin 1.4 is released, setOfNotNull will also be introduced, so we should add support for this function as well.

Fixed in 8587d4f.

val calleeExpression = expression.calleeExpression ?: return
val emptyCounterpart = exprsWithEmptyCounterparts[calleeExpression.text]

if (emptyCounterpart != null && expression.valueArguments.isEmpty()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of emptyCounterpart != null probably guard clause can be used like the following:

val emptyCounterpart = exprsWithEmptyCounterparts[calleeExpression.text] ?: return

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point! Fixed in 920ecd0.

@arturbosch
Copy link
Member

Hey @veyndan , can you take a look why a testcase is crashing?
We would like your contribution in the next release :).

@veyndan
Copy link
Contributor Author

veyndan commented Jul 27, 2020

Hey @arturbosch, the build is currently failing because of #2864 (comment). I've been swamped for the past couple of weeks, but I should have some time this coming weekend to try and fix the tests.

@schalkms schalkms merged commit e368d4c into detekt:master Jul 31, 2020
@veyndan veyndan deleted the 2850/empty-counterpart branch July 31, 2020 17:30
@arturbosch arturbosch modified the milestones: 1.11.0, 1.10.1 Aug 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add a rule to flag places where emptyList (and similar) can be used
6 participants