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

added AvoidRecompilingPatterns (Kotlin) including test #296

Merged
merged 22 commits into from Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
de508ae
fix for multi match because of multiple import matches
Feb 18, 2024
cc5687a
AvoidRecompilingXPathExpression. Convert applicable rules to Kotlin #185
jborgers Mar 1, 2024
3d3e39d
Merge branch 'master' into kotlin/AvoidRecompilingXPathExpression
jborgers Mar 8, 2024
7da8d59
Convert applicable rules to Kotlin #185,
jborgers Mar 8, 2024
7824328
Add TODO to EvaluateRules doc
jborgers Mar 8, 2024
1ed00dd
Merge remote-tracking branch 'origin/kotlin/AvoidDecimalAndChoiceForm…
jborgers Mar 11, 2024
99198af
Added good ThreadLocal example and removed 'TODO'
jborgers Mar 11, 2024
e921a27
added braces around 'x and y' for clarity
jborgers Mar 11, 2024
ae12b77
added AvoidRecompilingPatterns including test (Convert applicable rul…
Mar 12, 2024
7af02b9
Merge branch 'master' into kotlin/AvoidRecompilingPatterns
Mar 12, 2024
ac8957a
update kotlin progress table
Mar 12, 2024
58c759f
removed xpath version
jborgers Mar 14, 2024
7452395
removed other xpath version
jborgers Mar 14, 2024
74d572f
added testkt script
jborgers Mar 14, 2024
aca86ac
Merge remote-tracking branch 'origin/master' into kotlin/AvoidRecompi…
jborgers Mar 14, 2024
22eec3b
Merge pull request #295 from jborgers/kotlin/AvoidRecompilingXPathExp…
jborgers Mar 14, 2024
e12d3ca
update EvaluateRulesForKotlin
jborgers Mar 14, 2024
00adc32
Fix AvoidRecompilingXPathExpression Java Good example.
jborgers Mar 14, 2024
1f61132
Update EvaluateRulesForKotlin
jborgers Mar 14, 2024
c221ab7
added AvoidRecompilingPatterns including test (Convert applicable rul…
Mar 12, 2024
25bc928
remove deprecated version tags
Mar 15, 2024
a1a3807
Merge remote-tracking branch 'origin/kotlin/AvoidRecompilingPatterns'…
Mar 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
57 changes: 56 additions & 1 deletion src/main/resources/category/kotlin/common.xml
Expand Up @@ -105,7 +105,7 @@ class AvoidStringBuffer {
(: filter matches in functions :)
//ClassDeclaration//PrimaryExpression//SimpleIdentifier//T-Identifier[
(@Text="DecimalFormat" or @Text="ChoiceFormat")
and not(ancestor::FunctionDeclaration)
and not(ancestor::FunctionBody)
]
]]>
</value>
Expand Down Expand Up @@ -133,4 +133,59 @@ class Foo {
</example>
</rule>

<rule name="AvoidRecompilingPatterns"
class="net.sourceforge.pmd.lang.rule.XPathRule"
dfa="false"
language="kotlin"
message="Pattern.compile is used in a method. Compiling a regex pattern can be expensive, make it a constant or instance field."
typeResolution="true"
externalInfoUrl="${doc_root}/JavaCodePerformance.md#ireu02">
<description>A regular expression is compiled on every invocation. Problem: this can be expensive, depending on the length of the regular expression.&#13;
Solution: Usually a pattern is a literal, not dynamic and can be compiled only once. Assign it to a private constant or instance field.
java.util.Pattern objects are thread-safe so they can be shared among threads. (jpinpoint-rules)</description>
<priority>2</priority>
<properties>
<property name="tag" value="jpinpoint-rule" type="String" description="for-sonar"/>
<property name="version" value="3.1"/>
Copy link
Owner

Choose a reason for hiding this comment

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

remove version property, since it is deprecated in pmd-7

<property name="xpath">
<value><![CDATA[
(: check if java.util.regex imports exists as filter for same named classes in other packages :)
//ImportHeader[.//T-Identifier[@Text='java'] and .//T-Identifier[@Text='util'] and .//T-Identifier[@Text='regex']][1]/ancestor::KotlinFile

//FunctionBody//T-Identifier[@Text='Pattern' and ../../../PostfixUnarySuffix//T-Identifier[@Text='compile']
(: check for variables $action or ${action} in String of compile() call (first in possible call chain): allow dynamic input :)
and not(((../../..//ValueArgument)[1]//PostfixUnaryExpression//T-Identifier|(../../..//ValueArgument)[1]//T-LineStrRef)[
@Text=ancestor::FunctionDeclaration//FunctionValueParameter/Parameter/SimpleIdentifier/T-Identifier/@Text
or @Text=concat('$', ancestor::FunctionDeclaration//FunctionValueParameter/Parameter/SimpleIdentifier/T-Identifier/@Text)])
]
]]>
</value>
</property>
</properties>
<example>
<![CDATA[
import java.util.regex.Pattern

internal object Bad {
const val STR_PAT1: String = "[A-Z][a-z]+"

fun bad() {
val p1: Pattern = Pattern.compile(STR_PAT1) // bad
val p2: Pattern = Pattern.compile("(?=\\p{Lu})") // bad
val b: Boolean = p1.matcher("Start ").matches()
}
}

internal object Good {
val PAT1: Pattern = Pattern.compile("[A-Z][a-z]+")
val PAT2: Pattern = Pattern.compile("(?=\\p{Lu})")

fun good() {
val b: Boolean = PAT1.matcher("Start ").matches()
}
}
]]>
</example>
</rule>

</ruleset>
@@ -0,0 +1,6 @@
package com.jpinpoint.perf.lang.kotlin.ruleset.common;

import net.sourceforge.pmd.testframework.PmdRuleTst;

public class AvoidRecompilingPatternsTest extends PmdRuleTst {
}
@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8"?>
<test-data
xmlns="http://pmd.sourceforge.net/rule-tests"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">
<test-code>
<description>violation: Avoid recompiling patterns</description>
<expected-problems>3</expected-problems>
<expected-linenumbers>5,12,13</expected-linenumbers>
<code><![CDATA[
import java.util.regex.Pattern

class Foo {
fun createNameFromAction(action: String?) {
val p = Pattern.compile("(?=\\p{Lu})") // bad
}

companion object {
const val PATTERN: String = "^\\s*%s=(.*)"

fun methodBad(action: String?) {
val p = Pattern.compile("(?=\\p{Lu})") // bad: violation, should not be inside method, make static final
val s = Pattern.compile(PATTERN) // bad: violation
}
}
}
]]></code>
</test-code>
<test-code>
<description>no violation: Avoid recompiling patterns</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

object Bar {
const val PATTERN: String = "^\\s*%s=(.*)"

fun createEventNameFromAction(action: String): List<*> {
val q1 = Pattern.compile("($action?=\\p{Lu})") // ok, dynamic -- TODO make function for this?
val q2 = Pattern.compile("(" + action + "?=\\p{Lu})") // ok, dynamic
val q3 = Pattern.compile("(${action}?=\\p{Lu})") // ok, dynamic
val r = Pattern.compile(String.format(PATTERN, action)) // ok, dynamic
val t = Pattern.compile(action) // dynamic, so ok (fix for JPCC-63)

val actionSplit = q.split(action) // ok
val list: MutableList<Array<String>> = ArrayList()
list.add(actionSplit)
return list
}
}
]]></code>
</test-code>

<test-code>
<description>no violation: Avoid recompiling patterns, other compile</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
class GoodHere {
fun otherCompile() {
MyCompiler.compile("stuff")
}
}

internal object MyCompiler {
fun compile(s: String?) {
}
}
]]></code>
</test-code>

<test-code>
<description>violation: Avoid recompiling patterns, with chained operations, issue #180</description>
<expected-problems>2</expected-problems>
<expected-linenumbers>5,10</expected-linenumbers>
<code><![CDATA[
import java.util.regex.Pattern;

class Foo {
private fun isAmountAllowed(amount: String): Boolean {
return LIMIT > Pattern.compile(SEP).matcher(amount).replaceAll("").length() // bad
}

private val isAmountAllowed2: Boolean
get() {
val bla: String = Pattern.compile("[.|,]").matcher("5.000,00").replaceAll("") // bad
val b = (11 > bla.length)
return b
}

companion object {
private const val LIMIT = 11
private const val SEP = "[.|,]"
}
}
]]></code>
</test-code>

</test-data>