Allow to suppress issues if they are found in a function with a determined name#4148
Allow to suppress issues if they are found in a function with a determined name#4148BraisGabin merged 6 commits intomainfrom
Conversation
Codecov Report
@@ Coverage Diff @@
## main #4148 +/- ##
============================================
+ Coverage 84.22% 84.24% +0.01%
Complexity 3258 3258
============================================
Files 472 473 +1
Lines 10309 10322 +13
Branches 1820 1824 +4
============================================
+ Hits 8683 8696 +13
Misses 666 666
Partials 960 960
Continue to review full report at Codecov.
|
|
This would address the other part of #4080. |
picklebento
left a comment
There was a problem hiding this comment.
Thanks for the test coverage.
|
I'm moving this PR back to draft because I need #4176 merged first. |
| } | ||
| } | ||
| } | ||
| }) |
There was a problem hiding this comment.
I'm not adding any new test for the cases where you define parameters because all those cases are already tested in FunctionSignatureSpec already. Here I'm basically testing that the scope of the suppression is correct.
6db005f to
bde99ef
Compare
f71d8a5 to
800d094
Compare
|
|
||
| internal fun functionSuppressorFactory(rule: ConfigAware, bindingContext: BindingContext): Suppressor? { | ||
| val signatures = rule.valueOrDefault("ignoreFunction", emptyList<String>()).map(FunctionSignature::fromString) | ||
| return if (signatures.isNotEmpty()) { |
There was a problem hiding this comment.
Should you check here if the BindingContext is empty?
There was a problem hiding this comment.
🤔 yes and no. If you uses just the name of the function to suppress it we don't use the BindingContext. For that reason I don't check it. But maybe this is adding a new "mixed behaviour" that we don't want.
| for (rule in rules) { | ||
| rule.visitFile(file, bindingContext, compilerResources) | ||
| for (finding in filterSuppressedFindings(rule)) { | ||
| for (finding in filterSuppressedFindings(rule, bindingContext)) { |
There was a problem hiding this comment.
I just have one concern here: is this going to work for users that are running without type resolution?
If not, we should be aware that this will potentially create even more confusion (or maybe will push more people into looking into type resolution 🤔 ).
There was a problem hiding this comment.
The suppressors are not directly related with type solving. There are some suppressors that work without it as ignoreAnnotated other mixed as the one that this PR implements and maybe there will be others that only works with type resolution.
There was a problem hiding this comment.
There are some suppressors that work without it as ignoreAnnotated other mixed as the one that this PR implements and maybe there will be others that only works with type resolution.
How do we let the users know which suppressors works with TR or not (similarly to @RequiresTypeResolution)?
There was a problem hiding this comment.
I don't know how are we going to document this feature. #4162 would help with the discoverability but probably we will need a new page to talk about Suppressors.
800d094 to
5631bf0
Compare
a07caeb to
bda4de4
Compare
| private fun KtElement.isInFunctionNamed( | ||
| bindingContext: BindingContext, | ||
| functionMatchers: List<FunctionMatcher>, | ||
| ): Boolean { | ||
| return if (this is KtNamedFunction && functionMatchers.any { it.match(this, bindingContext) }) { | ||
| true | ||
| } else { | ||
| getStrictParentOfType<KtNamedFunction>()?.isInFunctionNamed(bindingContext, functionMatchers) ?: false | ||
| } | ||
| } |
There was a problem hiding this comment.
| private fun KtElement.isInFunctionNamed( | |
| bindingContext: BindingContext, | |
| functionMatchers: List<FunctionMatcher>, | |
| ): Boolean { | |
| return if (this is KtNamedFunction && functionMatchers.any { it.match(this, bindingContext) }) { | |
| true | |
| } else { | |
| getStrictParentOfType<KtNamedFunction>()?.isInFunctionNamed(bindingContext, functionMatchers) ?: false | |
| } | |
| } | |
| private fun KtElement.isInFunctionNamed( | |
| bindingContext: BindingContext, | |
| functionMatchers: List<FunctionMatcher>, | |
| ): Boolean = | |
| getParentOfType<KtNamedFunction>(false)?.let { function -> | |
| functionMatchers.any { it.match(function, bindingContext) } | |
| } ?: false |
There was a problem hiding this comment.
What do you think about writing in this way to avoid recursion? We can even inline this function.
There was a problem hiding this comment.
You can have a function inside another function. I prefer the recursive version. But I should add a test to demostraste that intention or any one could refactor this code later.
There was a problem hiding this comment.
I see your intention and it makes sense. It does not feel like the code in the existing implementation addresses the case where there is function nested inside another function though.
There was a problem hiding this comment.
It work. I added the tests and it works with nested functions :)
There was a problem hiding this comment.
After a second thought, you are right. My mind couldn't keep up with code today
| """.trimIndent() | ||
| ) | ||
| } | ||
| val binding by memoized { env.getContextForPaths(listOf(root)) } |
There was a problem hiding this comment.
Just to confirm, we can use BindingContext.EMPTY and the tests would still pass, because we don't require type resolution. Is that correct?
There was a problem hiding this comment.
Good point! It depends. If you only match using the name you don't need the BindingContext but if you want to match the parameters you need the BindingContext. I'm going to add some tests to demostrate this.
There was a problem hiding this comment.
Ups! I just reread my own comment: https://github.com/detekt/detekt/pull/4148/files/bda4de4f0177e4493dbd7af7019d209df2ae29fe#r725492307 That behaviour is tested on FunctionSignatureSpec.
I'm conflicted here. I want to write "functional-ish code". But if I do so the tests end up beening a bit crapy. I can't create a fake/mock of FunctionSignature and inject it.
There was a problem hiding this comment.
My assumption is that, if a feature does not require type resolution, we should write our tests as not requiring type resolution.
There was a problem hiding this comment.
I understand your comment now. You are completely right! I'm using now BindingContext.EMPTY
There are places where you don't care about an issue inside a determined function. One example is #3855
Fix #3855