Introduce Refaster rules for Sonar's java:S4635 rule (#1320)

As well as two related expressions that can be optimized.

See:
- https://sonarcloud.io/organizations/picnic-technologies/rules?open=java%3AS4635&rule_key=java%3AS4635
- 2615792731/java-checks/src/main/java/org/sonar/java/checks/StringOffsetMethodsCheck.java
This commit is contained in:
Stephan Schroevers
2024-09-16 09:15:12 +02:00
committed by GitHub
parent 26da67d1f5
commit 3d5ee10d93
3 changed files with 157 additions and 0 deletions

View File

@@ -244,4 +244,105 @@ final class StringRules {
return Utf8.encodedLength(str);
}
}
/** Prefer {@link String#indexOf(int, int)} over less efficient alternatives. */
static final class StringIndexOfChar {
@BeforeTemplate
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
int before(String string, int ch, int fromIndex) {
return string.substring(fromIndex).indexOf(ch);
}
@AfterTemplate
int after(String string, int ch, int fromIndex) {
return Math.max(-1, string.indexOf(ch, fromIndex) - fromIndex);
}
}
/** Prefer {@link String#indexOf(String, int)} over less efficient alternatives. */
static final class StringIndexOfString {
@BeforeTemplate
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
int before(String string, String substring, int fromIndex) {
return string.substring(fromIndex).indexOf(substring);
}
@AfterTemplate
int after(String string, String substring, int fromIndex) {
return Math.max(-1, string.indexOf(substring, fromIndex) - fromIndex);
}
}
// XXX: Once we compile Refaster templates with JDK 21 also suggest `String#indexOf(int, int,
// int)` and `String#indexOf(String, int, int)`.
/** Prefer {@link String#lastIndexOf(int, int)} over less efficient alternatives. */
static final class StringLastIndexOfChar {
@BeforeTemplate
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
int before(String string, int ch, int fromIndex) {
return string.substring(fromIndex).lastIndexOf(ch);
}
@AfterTemplate
int after(String string, int ch, int fromIndex) {
return Math.max(-1, string.lastIndexOf(ch) - fromIndex);
}
}
/** Prefer {@link String#lastIndexOf(String, int)} over less efficient alternatives. */
static final class StringLastIndexOfString {
@BeforeTemplate
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
int before(String string, String substring, int fromIndex) {
return string.substring(fromIndex).lastIndexOf(substring);
}
@AfterTemplate
int after(String string, String substring, int fromIndex) {
return Math.max(-1, string.lastIndexOf(substring) - fromIndex);
}
}
/** Prefer {@link String#lastIndexOf(int, int)} over less efficient alternatives. */
static final class StringLastIndexOfCharWithIndex {
@BeforeTemplate
int before(String string, int ch, int fromIndex) {
return string.substring(0, fromIndex).lastIndexOf(ch);
}
@AfterTemplate
int after(String string, int ch, int fromIndex) {
return string.lastIndexOf(ch, fromIndex - 1);
}
}
/** Prefer {@link String#lastIndexOf(String, int)} over less efficient alternatives. */
// XXX: The replacement expression isn't fully equivalent: in case `substring` is empty, then
// the replacement yields `fromIndex - 1` rather than `fromIndex`.
static final class StringLastIndexOfStringWithIndex {
@BeforeTemplate
int before(String string, String substring, int fromIndex) {
return string.substring(0, fromIndex).lastIndexOf(substring);
}
@AfterTemplate
int after(String string, String substring, int fromIndex) {
return string.lastIndexOf(substring, fromIndex - 1);
}
}
/** Prefer {@link String#startsWith(String, int)} over less efficient alternatives. */
static final class StringStartsWith {
@BeforeTemplate
@SuppressWarnings("java:S4635" /* This violation will be rewritten. */)
boolean before(String string, String prefix, int fromIndex) {
return string.substring(fromIndex).startsWith(prefix);
}
@AfterTemplate
boolean after(String string, String prefix, int fromIndex) {
return string.startsWith(prefix, fromIndex);
}
}
}

View File

@@ -96,4 +96,32 @@ final class StringRulesTest implements RefasterRuleCollectionTestCase {
int testUtf8EncodedLength() {
return "foo".getBytes(UTF_8).length;
}
int testStringIndexOfChar() {
return "foo".substring(1).indexOf('a');
}
int testStringIndexOfString() {
return "foo".substring(1).indexOf("bar");
}
int testStringLastIndexOfChar() {
return "foo".substring(1).lastIndexOf('a');
}
int testStringLastIndexOfString() {
return "foo".substring(1).lastIndexOf("bar");
}
int testStringLastIndexOfCharWithIndex() {
return "foo".substring(0, 2).lastIndexOf('a');
}
int testStringLastIndexOfStringWithIndex() {
return "foo".substring(0, 2).lastIndexOf("bar");
}
boolean testStringStartsWith() {
return "foo".substring(1).startsWith("bar");
}
}

View File

@@ -96,4 +96,32 @@ final class StringRulesTest implements RefasterRuleCollectionTestCase {
int testUtf8EncodedLength() {
return Utf8.encodedLength("foo");
}
int testStringIndexOfChar() {
return Math.max(-1, "foo".indexOf('a', 1) - 1);
}
int testStringIndexOfString() {
return Math.max(-1, "foo".indexOf("bar", 1) - 1);
}
int testStringLastIndexOfChar() {
return Math.max(-1, "foo".lastIndexOf('a') - 1);
}
int testStringLastIndexOfString() {
return Math.max(-1, "foo".lastIndexOf("bar") - 1);
}
int testStringLastIndexOfCharWithIndex() {
return "foo".lastIndexOf('a', 2 - 1);
}
int testStringLastIndexOfStringWithIndex() {
return "foo".lastIndexOf("bar", 2 - 1);
}
boolean testStringStartsWith() {
return "foo".startsWith("bar", 1);
}
}