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

Better Support for Guard Clauses in ThrowsCount Rule #2876

Merged
merged 9 commits into from
Jul 16, 2020
Merged

Better Support for Guard Clauses in ThrowsCount Rule #2876

merged 9 commits into from
Jul 16, 2020

Conversation

amitd291
Copy link
Contributor

@amitd291 amitd291 commented Jul 15, 2020

This PR achieves the following:

  • Make GuardClauses extension functions generic to support both return and throw expressions
  • Fix ThrowsCount scenarios related to guard clauses and add tests for the same
  • Reuse GuardClauses code for ThrowsCount rule

amitd291 added 4 commits July 15, 2020 23:49
…xpressions, fix throw count scenarios related to guard clauses and add tests, reuse guard clause code for throw count rule
@amitd291 amitd291 changed the title Better Support for Guard Clauses in ThrowCount Better Support for Guard Clauses in ThrowsCount Rule Jul 15, 2020
@codecov
Copy link

codecov bot commented Jul 15, 2020

Codecov Report

Merging #2876 into master will decrease coverage by 0.11%.
The diff coverage is 63.63%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #2876      +/-   ##
============================================
- Coverage     80.28%   80.17%   -0.12%     
  Complexity     2417     2417              
============================================
  Files           421      421              
  Lines          7359     7358       -1     
  Branches       1346     1343       -3     
============================================
- Hits           5908     5899       -9     
- Misses          756      764       +8     
  Partials        695      695              
Impacted Files Coverage Δ Complexity Δ
.../io/gitlab/arturbosch/detekt/rules/GuardClauses.kt 25.00% <30.00%> (-62.50%) 0.00 <0.00> (ø)
...itlab/arturbosch/detekt/rules/style/ThrowsCount.kt 95.45% <90.90%> (-4.55%) 6.00 <1.00> (-1.00)
...itlab/arturbosch/detekt/rules/style/ReturnCount.kt 95.12% <100.00%> (ø) 11.00 <0.00> (+1.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 5a0fe62...b5b838b. Read the comment docs.

}
throw Exception()
}
"""
Copy link
Member

Choose a reason for hiding this comment

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

It looks like all test code templates need 4 extra spaces

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@arturbosch good catch, I have fixed it.


throw Exception()
}
"""
Copy link
Member

Choose a reason for hiding this comment

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

Here it is right

} else {
if (!it.isGuardClause()) {
firstNonGuardFound = true
inline fun <reified T : KtExpression> KtNamedFunction.yieldStatementsSkippingGuardClauses(): Sequence<KtExpression> =
Copy link
Member

Choose a reason for hiding this comment

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

Should this return T instead?

Copy link
Contributor Author

@amitd291 amitd291 Jul 16, 2020

Choose a reason for hiding this comment

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

@arturbosch sorry if it was not clear, but the generic type T is for the child expression which can be either return or throw. These expressions can be of the sub-types KtReturnExpression or KtThrowExpression. The generic finds its purpose in the following line of code:

val descendantExpr = this.findDescendantOfType<T>() ?: return false

And on the consumer side the code looks like the following:

// in ReturnCount.kt
yieldStatementsSkippingGuardClauses<KtReturnExpression>()
// in ThrowsCount.kt
yieldStatementsSkippingGuardClauses<KtThrowExpression>()

Whereas the return type of yieldStatementsSkippingGuardClauses method is a Sequence of parent statements, which is always of the type KtExpression.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks for the explanation.

@arturbosch arturbosch added this to the 1.11.0 milestone Jul 15, 2020
Comment on lines 17 to 22
} else {
if (!it.isGuardClause<T>()) {
firstNonGuardFound = true
yield(it)
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
} else {
if (!it.isGuardClause<T>()) {
firstNonGuardFound = true
yield(it)
}
}
} else if (!it.isGuardClause<T>()) {
firstNonGuardFound = true
yield(it)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@schalkms thanks for the suggestion, it has been applied.

}

fun KtExpression.isElvisOperatorGuardClause(): Boolean {
val elvisExpr = this.parent as? KtBinaryExpression
return elvisExpr?.operationToken == KtTokens.ELVIS
val elvisExpr = this.findDescendantOfType<KtBinaryExpression>() ?: return false
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure if I really understand this change. Could you please shed some light into this?

Copy link
Contributor Author

@amitd291 amitd291 Jul 16, 2020

Choose a reason for hiding this comment

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

@schalkms
Earlier isElvisOperatorGuardClause was being called as an extension for the child return expression, and now I have modified it to be called as an extension on the parent statement (which contains the return / throw child expression)
Firstly it makes it generic for both return and throw expressions, as it is called on the parent. And secondly it sounds more meaningful, as the the parent statement is a guard clause (and not the child).

@amitd291
Copy link
Contributor Author

amitd291 commented Jul 16, 2020

@arturbosch @schalkms
I have a question on the code coverage check. No matter how many spek test cases I try to add (exhaustively covering most of the code branches), it always says coverage has degraded. Any hints on how to fix this?

@arturbosch
Copy link
Member

arturbosch commented Jul 16, 2020

@amitdash291 for GuardClauses it seems that jacoco does not probably instrument the bytecode. Maybe some edge case for Kotlin sequence generators. Their Kotlin support is still fresh.
Sometimes we also have some problems with CI caching ^^.
PR looks fine, I'll merge it, thanks!

@arturbosch arturbosch merged commit 2de31f1 into detekt:master Jul 16, 2020
@amitd291
Copy link
Contributor Author

@amitdash291 for GuardClauses it seems that jacoco does not probably instrument the bytecode. Maybe some edge case for Kotlin sequence generators. Their Kotlin support is still fresh.
Sometimes we also have some problems with CI caching ^^.
PR looks fine, I'll merge it, thanks!

@arturbosch got it, the clarification about coverage issue.
Thanks for merging this PR 👍

@arturbosch arturbosch modified the milestones: 1.11.0, 1.10.1 Aug 2, 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.

3 participants