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

Allow to suppress issues if they are found in a function with a determined name #4148

Merged
merged 6 commits into from
Nov 23, 2021

Conversation

BraisGabin
Copy link
Member

There are places where you don't care about an issue inside a determined function. One example is #3855

Fix #3855

@codecov
Copy link

codecov bot commented Oct 2, 2021

Codecov Report

Merging #4148 (a05c230) into main (c66b8d6) will increase coverage by 0.01%.
The diff coverage is 100.00%.

Impacted file tree graph

@@             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              
Impacted Files Coverage Δ
...ab/arturbosch/detekt/core/config/ValidateConfig.kt 100.00% <100.00%> (ø)
...osch/detekt/core/suppressors/FunctionSuppressor.kt 100.00% <100.00%> (ø)
...b/arturbosch/detekt/core/suppressors/Suppressor.kt 71.42% <100.00%> (+2.19%) ⬆️

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 c66b8d6...a05c230. Read the comment docs.

@chao2zhang
Copy link
Member

This would address the other part of #4080.

Copy link
Member

@chao2zhang chao2zhang left a 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.

@chao2zhang chao2zhang added this to the 1.19.0 milestone Oct 3, 2021
@BraisGabin BraisGabin changed the base branch from main to pre-fix-3855 October 9, 2021 14:06
@BraisGabin BraisGabin marked this pull request as draft October 9, 2021 14:07
@BraisGabin
Copy link
Member Author

I'm moving this PR back to draft because I need #4176 merged first.

}
}
}
})
Copy link
Member Author

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.


internal fun functionSuppressorFactory(rule: ConfigAware, bindingContext: BindingContext): Suppressor? {
val signatures = rule.valueOrDefault("ignoreFunction", emptyList<String>()).map(FunctionSignature::fromString)
return if (signatures.isNotEmpty()) {
Copy link
Member

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?

Copy link
Member Author

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)) {
Copy link
Member

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 🤔 ).

Copy link
Member Author

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.

Copy link
Member

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)?

Copy link
Member Author

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.

Comment on lines +31 to +40
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
}
}
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
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

Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

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 :)

Copy link
Member

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)) }
Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Member

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.

Copy link
Member Author

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

@chao2zhang chao2zhang changed the title Allow to suppress issues if they are find in a function with a determined name Allow to suppress issues if they are found in a function with a determined name Nov 17, 2021
@BraisGabin BraisGabin merged commit 5fa01a9 into main Nov 23, 2021
@BraisGabin BraisGabin deleted the fix-3855 branch November 23, 2021 17:32
@cortinico cortinico added the notable changes Marker for notable changes in the changelog label Nov 23, 2021
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 suppressors
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Some exclusion mechanism for NullableToStringCall
4 participants