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

Adding support for full method signatures in ForbiddenMethodCall #3505

Conversation

DcortezMeleth
Copy link
Contributor

This PR is for now a proof of concept for #3498.

Namely it's adding support for full method signatures (like java.time.LocalDate.now(java.time.Clock) in ForbiddenMethodCall rule.

Copy link
Member

@cortinico cortinico left a comment

Choose a reason for hiding this comment

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

Great work 💪 We need a bit of fine tuning and I think we should be able to merge this

Comment on lines 50 to 54
val groups = METHOD_PARAM_REGEX.find(methodSignature)?.groups
val methodName = groups?.get(METHOD_GROUP_NAME)?.value
val params = groups?.get(METHOD_PARAM_GROUP_NAME)?.value?.split(",")
?.map { it.trim() }
?.filter { it.isNotBlank() }
Copy link
Member

Choose a reason for hiding this comment

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

Please replace with something like:

Suggested change
val groups = METHOD_PARAM_REGEX.find(methodSignature)?.groups
val methodName = groups?.get(METHOD_GROUP_NAME)?.value
val params = groups?.get(METHOD_PARAM_GROUP_NAME)?.value?.split(",")
?.map { it.trim() }
?.filter { it.isNotBlank() }
val groups = methodSignature.split('(',')')
val methodName = groups[0].trim()
val params = if (groups.size <= 1) {
null
} else {
groups[1].split(",")
.map(String::trim)
.filter(String::isNotBlank)
}

Regexes are hard to maintain in the long run, especially for shared codebases like Detekt.

Copy link
Member

Choose a reason for hiding this comment

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

If you wish, you can also create a util function inside the detekt-psi-utils package and write a unit test (like a .parseFqNameAndParams() or so).

Copy link
Member

Choose a reason for hiding this comment

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

Probably, because it's not that easy because some functions can be called `like, this`(Int).

And, if we want to move out this code, it should be placed in tooling utils instead of psi utils, right? This code is to parse the configuration. It's not related with psi.

Copy link
Member

Choose a reason for hiding this comment

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

it should be placed in tooling utils instead of psi utils, right?

I checked and detekt-psi-utils is already imported inside the -rules modules as an api dependency. The detekt-tooling is not and I don't think it's worth to add a module dependency for a function.

Not to be done in this PR, but I think we should rename detekt-psi-utils to detekt-rules-utils (given that inside there is also not PSI related utils) or create a separate detekt-rules-utils just for those functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea. My first thought was to use regex and I was so fixed on this approach that I missed simpler one.

forbiddenMethods
.filter { fqName == it.first }
.forEach {
if (it.second == null || it.second == methodParameterFqNames) {
Copy link
Member

Choose a reason for hiding this comment

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

To tackle the java.time.LocalDate.of(Int, Int, Int), you should make sure you run a param-by-param comparison of the two lists and check both the fully qualified and the simple name:

val configParams = it.second
val match = methodParameterFqNames.foldIndexed(true) { index: Int, acc: Boolean, param: String ->
    acc && (param == configParams[index] || param.split('.').last() == configParams[index])
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see one issue here. Won't we create inconsistency between how method should be defined (only fully qualified) and params (fully qualified or just class name)?
Other than that it seems ok, as it will allow you to shorten definition and only use qualified names when false-positives would be encountered otherwise.

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 one issue here. Won't we create inconsistency between how method should be defined (only fully qualified) and params (fully qualified or just class name)?
Other than that it seems ok, as it will allow you to shorten definition and only use qualified names when false-positives would be encountered otherwise.

You're right. I think we could then revert to always be explicit. Maybe add a note example in the doc so users are aware on how to us it properly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@DcortezMeleth DcortezMeleth force-pushed the feature/ForbiddenMethodCall_full_method_signature branch from 83be499 to 12ab206 Compare February 28, 2021 13:01
@DcortezMeleth
Copy link
Contributor Author

I have updated PR following your instructions.
I didn't extract methods params parsing to util as I wasn't sure if it will be useful anywhere else but if you feel that we should go for it just tell me where is the best place to put it inside util module.

@codecov
Copy link

codecov bot commented Feb 28, 2021

Codecov Report

Merging #3505 (f9c45fe) into master (c90eafe) will increase coverage by 0.01%.
The diff coverage is 87.50%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #3505      +/-   ##
============================================
+ Coverage     77.62%   77.64%   +0.01%     
- Complexity     2806     2810       +4     
============================================
  Files           461      461              
  Lines          8672     8691      +19     
  Branches       1676     1683       +7     
============================================
+ Hits           6732     6748      +16     
  Misses         1035     1035              
- Partials        905      908       +3     
Impacted Files Coverage Δ Complexity Δ
.../gitlab/arturbosch/detekt/rules/MethodSignature.kt 58.82% <71.42%> (+8.82%) 0.00 <0.00> (ø)
...turbosch/detekt/rules/style/ForbiddenMethodCall.kt 91.89% <94.11%> (-0.11%) 14.00 <0.00> (+4.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 c90eafe...aab67c1. Read the comment docs.

@cortinico
Copy link
Member

I wasn't sure if it will be useful anywhere else but if you feel that we should go for it just tell me

Having the function extracted was more for testing reason. It's easier to write a couple of unit tests in a separate module for just that function. Anyway that's not a hard requirement for merging this PR.

@DcortezMeleth DcortezMeleth force-pushed the feature/ForbiddenMethodCall_full_method_signature branch 2 times, most recently from 7c95ca9 to 527dede Compare February 28, 2021 20:14
@@ -8,6 +8,8 @@ import io.gitlab.arturbosch.detekt.api.Issue
import io.gitlab.arturbosch.detekt.api.Rule
import io.gitlab.arturbosch.detekt.api.Severity
import io.gitlab.arturbosch.detekt.api.internal.valueOrDefaultCommaSeparated
import io.gitlab.arturbosch.detekt.rules.extractMethodNameAndParams
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
Copy link
Member

Choose a reason for hiding this comment

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

Not necessarily an issue for this PR, because getJetTypeFqName is used at several places. However, the package path looks very weird because js appears.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As I mentioned it is my first meeting with kotlin reflection and it was the solution I have found but if you have some idea how to achieve it with other method I will be happy to try that out.

Copy link
Member

Choose a reason for hiding this comment

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

I can investigate further on why this is needed. I don't think this would block merging this PR

@chao2zhang
Copy link
Member

What do we think about methods with default arguments? If fun foo(bar: Int, baz: Int = 5) is in the configuration and we encounter foo(5), should we also report that?

(This PR looks good in general, this might be an edge case that we can follow up)

@DcortezMeleth DcortezMeleth force-pushed the feature/ForbiddenMethodCall_full_method_signature branch from 527dede to aab67c1 Compare March 1, 2021 06:09
@DcortezMeleth
Copy link
Contributor Author

As for the methods with default parameters I think we should report them all, because otherwise if we would like to forbid method with more than one default parameter, we would have to cover or possible combinations in configuration.
Also typically we wan't to discourage usage of a method and not the certain way of calling it, and even if we did, I feel that this should be done be a separate rule then.

I have added test case with default parameter. It worked without changes in the rule logic.

@DcortezMeleth
Copy link
Contributor Author

Thanks for approval. If I can do anything else to help you with this PR please let me know!

Copy link
Member

@schalkms schalkms left a comment

Choose a reason for hiding this comment

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

If @BraisGabin approves, I think we can merge this.
Thanks for this awesome PR. It's a nice improvement of this rule.

@BraisGabin
Copy link
Member

Great work!

@BraisGabin BraisGabin merged commit c5f65a9 into detekt:master Mar 2, 2021
@DcortezMeleth
Copy link
Contributor Author

No problem. I'm happy that I could have helped.

This was referenced Mar 11, 2021
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.

None yet

5 participants