-
-
Notifications
You must be signed in to change notification settings - Fork 760
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
Allow to suppress issues if they are found in a function with a determined name #4148
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. |
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.
Thanks for the test coverage.
detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/suppressors/Suppressors.kt
Outdated
Show resolved
Hide resolved
detekt-core/src/main/kotlin/io/gitlab/arturbosch/detekt/core/suppressors/FunctionSuppressor.kt
Outdated
Show resolved
Hide resolved
I'm moving this PR back to draft because I need #4176 merged first. |
} | ||
} | ||
} | ||
}) |
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'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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should you check here if the BindingContext
is empty?
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.
🤔 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.
@@ -120,7 +120,7 @@ internal class Analyzer( | |||
fun executeRules(rules: List<BaseRule>) { | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It work. I added the tests and it works with nested functions :)
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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