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

Add SuspendFunSwallowedCancellation rule #5666

Merged
merged 9 commits into from Apr 5, 2023
Merged

Conversation

atulgpt
Copy link
Contributor

@atulgpt atulgpt commented Jan 4, 2023

Fixes #5468

More details:

This PR tries to detekt, all the suspend fun inside a runCatching block. Currently, I am finding each suspension point(suspend fun) inside the runCatching block and then for each suspension point trying to backpropagate if I can reach back to the original runCatching block or not.
Cases, where I am not able to reach, are as follows (these cases will not be reported a code smell for the current runCatching block)

  1. If the suspension point is called by lambda which is not an inline function
  2. If suspension point is called by fun which is inline but lambda, where that suspension point is present, is not inlined lambda(in other words lambda is marked with noinline or crossinline)

If any of the above points gets encountered, I break the backpropagation and then check if the returned function descriptor is the same as the current runCatching block(I am not checking against fqName as there could be multiple nested runCatching and then deeper suspension point will be reported against all the parent runCatching)

I have created this draft PR for early feedback and suggestion on improving the propagation around TCs. Although I agree that I am propagation a runCatching..suspend fun from top to bottom then bottom to top, I want to know if should I optimise this(do this in single propagation(This will require custom propagation of child or some other logic)) or is it okay. I'm fine either way

Update:
Now in the commit, I have simplified the traversal logic and only going inside a function for traversal when that function is inline or value parameter of the function is not marked noinline or crossinline(This helped in preventing the backpropagation from the suspension point)

@github-actions github-actions bot added the rules label Jan 4, 2023
@github-actions
Copy link

github-actions bot commented Jan 4, 2023

Messages
📖 Thanks for adding a new rule to detekt ❤️
We detekted that you added tests, to your rule, that's awesome!

We detekted that you updated the default-detekt-config.yml file, that's awesome!

Generated by 🚫 dangerJS against d7e1309

Copy link
Member

@TWiStErRob TWiStErRob left a comment

Choose a reason for hiding this comment

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

Really nice, looks pretty advanced, looking at the issue description I didn't expect this complexity.

@atulgpt atulgpt marked this pull request as ready for review January 5, 2023 23:12
@atulgpt atulgpt changed the title Add SuspendFunInsideRunCatching rule Add SuspendFunSwallowedCancellation rule Jan 5, 2023
Copy link
Member

@TWiStErRob TWiStErRob left a comment

Choose a reason for hiding this comment

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

Feels like the code is getting shorter, but definitely simpler, nice one!

@atulgpt
Copy link
Contributor Author

atulgpt commented Jan 11, 2023

Feels like the code is getting shorter, but definitely simpler, nice one!

Thanks

@codecov
Copy link

codecov bot commented Jan 12, 2023

Codecov Report

Merging #5666 (d7e1309) into main (6d7e7e6) will decrease coverage by 0.01%.
The diff coverage is 82.69%.

@@             Coverage Diff              @@
##               main    #5666      +/-   ##
============================================
- Coverage     84.59%   84.58%   -0.01%     
- Complexity     3790     3816      +26     
============================================
  Files           546      547       +1     
  Lines         12918    12969      +51     
  Branches       2268     2286      +18     
============================================
+ Hits          10928    10970      +42     
- Misses          861      862       +1     
- Partials       1129     1137       +8     
Impacted Files Coverage Δ
...ules/coroutines/SuspendFunSwallowedCancellation.kt 82.00% <82.00%> (ø)
...osch/detekt/rules/coroutines/CoroutinesProvider.kt 100.00% <100.00%> (ø)

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

@BraisGabin
Copy link
Member

I was out for some time and there are a lot of comments on these PR. Is there any blockers to merge this? Can I help to unblock something?

To me the tests feel a bit too long but this rule is a clear win for our users so we can improve that in later PRs. I mean, this implements just "half" issue. The runCatching thing. We need to implement the try/catch too.

@BraisGabin BraisGabin added this to the 1.23.0 milestone Feb 21, 2023
@atulgpt
Copy link
Contributor Author

atulgpt commented Feb 21, 2023

Hi @BraisGabin try-catch was agreed to be implemented as a separate PR(that's why the generic name SuspendFunSwallowedCancellation). Your input Is needed for the comment(context: Currently I am allowing false negative while addressing that comment will favour false positive instead of false negative) cc @TWiStErRob

@TWiStErRob
Copy link
Member

@BraisGabin note: unresolved conversations are unresolved, hence the name ;)

atulgpt and others added 9 commits February 25, 2023 04:24
Add suggestion from code review

Co-authored-by: Róbert Papp <papp.robert.s@gmail.com>
Rename rule to SuspendFunSwallowedCancellation
Add new TCs
Check shouldPropagate inside while checking for loop
Simplify if else block
Make the local function
Early return of the declaration of the variable
Report violating runCatching block instead of suspend call
Assert location in tests
Add TCs for suspend fun noinline and crossinline lambda
Copy link
Member

@BraisGabin BraisGabin left a comment

Choose a reason for hiding this comment

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

LGTM

@atulgpt
Copy link
Contributor Author

atulgpt commented Mar 14, 2023

@BraisGabin note: unresolved conversations are unresolved, hence the name ;)

Hi @TWiStErRob can you check once again? I have added the TC as well for your case and it is passing

@TWiStErRob
Copy link
Member

I think it's sufficiently covered with tests, so let's release it and see if false positives/negatives come up. I would add a note in the issue description something like:

* Note: This is a very complex topic, so there are ought to be
* false positive/false negative findings, if you come across one,
* please report in the [detekt issue tracker](https://github.com/detekt/detekt/issues).

I know this is the default expected behavior, but in the case of this rule, it's even more important to not just "suppress it in my case", but discuss usage patterns we didn't think of.

@cortinico
Copy link
Member

I think it's sufficiently covered with tests, so let's release it and see if false positives/negatives come up.

Merging as we want this included in 1.23 👍

@cortinico cortinico merged commit a3c2bbc into detekt:main Apr 5, 2023
@atulgpt atulgpt deleted the fixes-5468 branch April 5, 2023 17:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
notable changes Marker for notable changes in the changelog rules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Don't allow runCatching with supend functions inside
5 participants