-
-
Notifications
You must be signed in to change notification settings - Fork 755
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
Add UnnecessaryLambda
rule
#6005
Conversation
This code and TC is taken from intellij-community [convertLambdaToReference](https://github.com/JetBrains/intellij-community/blob/master/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertLambdaToReferenceIntention.kt) intention
Codecov Report
@@ Coverage Diff @@
## main #6005 +/- ##
============================================
- Coverage 84.65% 84.38% -0.28%
- Complexity 3840 3911 +71
============================================
Files 549 550 +1
Lines 13068 13216 +148
Branches 2305 2378 +73
============================================
+ Hits 11063 11152 +89
- Misses 868 888 +20
- Partials 1137 1176 +39
... and 1 file with indirect coverage changes Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
UnnecessaryLambda
rule
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rule logic is quite intricated. Can we vendor the ConvertLambdaToReferenceIntention.kt
file and keep the rule logic smaller?
...rformance/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/performance/UnnecessaryLambda.kt
Outdated
Show resolved
Hide resolved
...rformance/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/performance/UnnecessaryLambda.kt
Outdated
Show resolved
Hide resolved
...rformance/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/performance/UnnecessaryLambda.kt
Outdated
Show resolved
Hide resolved
...rformance/src/main/kotlin/io/gitlab/arturbosch/detekt/rules/performance/UnnecessaryLambda.kt
Outdated
Show resolved
Hide resolved
…detekt/rules/performance/UnnecessaryLambda.kt Co-authored-by: Nicola Corti <corti.nico@gmail.com>
Hi @cortinico, currently, I have exactly the same code as But if you still feel that it should in a separate file then I will change this |
Add description in code smell message Assert message in TCs
Hi, @cortinico / @BraisGabin any idea how to run tests against different Kotlin versions or with different Kotlin compiler flags? |
* fun toDomain(i: Int) = i.toString() | ||
* listOf(1).map { toDomain(it) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this code compliant? When I put this snippet into a kotlin file it does not compile.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you put
fun toDomain(i: Int) = i.toString()
listOf(1).map { toDomain(it) }
both the lines?
* Avoid creating unnecessary lambda to call a function where function reference can be used. This may have performance | ||
* penalty when compared to directly passing lambda variable. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have documentation, benchmarks that we could link to further explain the issue? I do not like that we state this as a fact and do not show proof. People might blindly follow the advice without second guessing it. I know I would.
I came across this post. I have no idea how credible it is. Does the performance penalty depend on the kotlin compiler version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And applies the performance penalty to all output targets?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, @marschwar, I did not run any benchmark on it. My initial motivation was to prohibit below type of use-case
fun test(lambda: () -> Unit) {
thread {
lambda()
}.start()
}
and prompt below alternate code
fun test(lambda: () -> Unit) {
thread(block = lambda).start()
}
But yes I agree that it will not be beneficial for all the use cases. I have also added This **may** have
implying that it will not be the case always.
Finally, this rule promotes function ref for all the cases for consistency. But I am also open to suggestions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think your original use case is quite different to what this ended up being. I am sorry, I should have made those points before you put in all the work.
I do not think that we should promote function references as there are many cases where it is not possible to use a function reference and therefor you might end up with a mix of function refs and lambdas in the same map chain.
Just out of curiosity I decompiled the generated kotlin byte code for the compliant and non-compliant version with IntelliJ and ended up with exactly the same java code. I do not have any experience with this feature but at first sight I do not think detekt should claim that this is or may be a performance issue unless we know for a fact in which circumstances that is the case.
BTW: Your original use case actually does generate an extra anonymous function in the non-compliant version making it a good candidate for a rule.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interestingly the kotlin compiler seems to do some optimizations as both functions below are decompiled into nearly the same java code.
fun compliant(lambda: (Int) -> Unit) {
repeat(3, lambda)
}
// and
fun nonCompliant(lambda: (Int) -> Unit) {
repeat(3) { lambda(it) }
}
In this case it may just be a matter of style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is because repeat
is inline fun
I think that this case might be worth to have the copied logic in a separate file. The reason is that it's quite extensive and hard to isolate. So if we need to update the vendored logic, it should be easier |
To me this is not a performance rule. It is a style rule. Probably a controversial style rule. But I think like the idea of this rule. |
Hi @BraisGabin / @marschwar should I move this rule to style rule section and remove the mention of performance part? |
I think it should be moved to the style rule set. But before you put in more work we should decide on the rule focus. should we do that on the original issue? |
Moving the conversation back to the issue: #5836 (comment) so we can talk more about it there. Blocking meanwhile. |
I believe this PR is obsolete now since we decided to go back to the drawing board. Can we close it @atulgpt ? |
Implements #5836