diff --git a/idea/resources/inspectionDescriptions/ReplaceStringFormatWithLiteral.html b/idea/resources/inspectionDescriptions/ReplaceStringFormatWithLiteral.html
new file mode 100644
index 00000000000..b4f58789dfe
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/ReplaceStringFormatWithLiteral.html
@@ -0,0 +1,5 @@
+
+
+This inspection reports String.format call can be replaced with string templates.
+
+
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 430258c85a9..4211a54f10d 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -2882,6 +2882,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
language="kotlin"
/>
+
+
+
+
+
+
+
+
+
+
+
+
String.format("foo is %s, bar is %s.", foo, bar)
+}
+
+class Foo(private val value: Int) : Formattable {
+ override fun formatTo(formatter: Formatter?, flags: Int, width: Int, precision: Int) {
+ formatter?.out()?.append("[$value]")
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/invalidArgs.kt b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/invalidArgs.kt
new file mode 100644
index 00000000000..6306240bdff
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/invalidArgs.kt
@@ -0,0 +1,8 @@
+// PROBLEM: none
+// WITH_RUNTIME
+
+fun test() {
+ val foo = 1
+
+ String.format("foo is %s, bar is %s.", foo)
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat.kt b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat.kt
new file mode 100644
index 00000000000..8d0ace6c703
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat.kt
@@ -0,0 +1,8 @@
+// RUNTIME_WITH_FULL_JDK
+
+fun test() {
+ val foo = 1
+ val bar = 2
+
+ java.lang.String.format("foo is %s, bar is %s.", foo, bar)
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat.kt.after b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat.kt.after
new file mode 100644
index 00000000000..46a31942d27
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat.kt.after
@@ -0,0 +1,8 @@
+// RUNTIME_WITH_FULL_JDK
+
+fun test() {
+ val foo = 1
+ val bar = 2
+
+ "foo is $foo, bar is $bar."
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat2.kt b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat2.kt
new file mode 100644
index 00000000000..88464b30207
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat2.kt
@@ -0,0 +1,10 @@
+// RUNTIME_WITH_FULL_JDK
+
+import java.lang.String.format
+
+fun test() {
+ val foo = 1
+ val bar = 2
+
+ format("foo is %s, bar is %s.", foo, bar)
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat2.kt.after b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat2.kt.after
new file mode 100644
index 00000000000..78aa96796d9
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat2.kt.after
@@ -0,0 +1,10 @@
+// RUNTIME_WITH_FULL_JDK
+
+import java.lang.String.format
+
+fun test() {
+ val foo = 1
+ val bar = 2
+
+ "foo is $foo, bar is $bar."
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/noArgs.kt b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/noArgs.kt
new file mode 100644
index 00000000000..a28c1adb160
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/noArgs.kt
@@ -0,0 +1,6 @@
+// PROBLEM: none
+// WITH_RUNTIME
+
+fun test() {
+ String.format("%%")
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/notStringPlaceFolder.kt b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/notStringPlaceFolder.kt
new file mode 100644
index 00000000000..1d48d77fb56
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/notStringPlaceFolder.kt
@@ -0,0 +1,9 @@
+// PROBLEM: none
+// WITH_RUNTIME
+
+fun test() {
+ val foo = 1
+ val bar = 2
+
+ String.format("foo is %s, bar is %d.", foo, bar)
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/notStringPlaceFolder2.kt b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/notStringPlaceFolder2.kt
new file mode 100644
index 00000000000..096d3b7680b
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/notStringPlaceFolder2.kt
@@ -0,0 +1,9 @@
+// PROBLEM: none
+// WITH_RUNTIME
+
+fun test() {
+ val foo = 1
+ val bar = 2
+
+ String.format("foo is %s, bar is %s.%n", foo, bar)
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/rawStringFormat.kt b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/rawStringFormat.kt
new file mode 100644
index 00000000000..b2ca510d5c5
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/rawStringFormat.kt
@@ -0,0 +1,9 @@
+// PROBLEM: none
+// WITH_RUNTIME
+
+fun test() {
+ val foo = 1
+ val bar = 2
+
+ String.format("""foo is %s, bar is %s.%n""", foo, bar)
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/simple.kt b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/simple.kt
new file mode 100644
index 00000000000..ffff1ad8ece
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/simple.kt
@@ -0,0 +1,12 @@
+// WITH_RUNTIME
+// HIGHLIGHT: INFORMATION
+
+fun test() {
+ val foo = 1
+
+ String.format("foo is %s, bar is %s.", foo, Bar().value)
+}
+
+class Bar {
+ val value = 2
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/simple.kt.after b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/simple.kt.after
new file mode 100644
index 00000000000..82e6bf73e6a
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/simple.kt.after
@@ -0,0 +1,12 @@
+// WITH_RUNTIME
+// HIGHLIGHT: INFORMATION
+
+fun test() {
+ val foo = 1
+
+ "foo is $foo, bar is ${Bar().value}."
+}
+
+class Bar {
+ val value = 2
+}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
index b5fd01b1f66..86c06e1af8f 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
@@ -4148,6 +4148,64 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
}
}
+ @TestMetadata("idea/testData/inspectionsLocal/replaceStringFormatWithLiteral")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class ReplaceStringFormatWithLiteral extends AbstractLocalInspectionTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInReplaceStringFormatWithLiteral() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/replaceStringFormatWithLiteral"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("formattableArgs.kt")
+ public void testFormattableArgs() throws Exception {
+ runTest("idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/formattableArgs.kt");
+ }
+
+ @TestMetadata("invalidArgs.kt")
+ public void testInvalidArgs() throws Exception {
+ runTest("idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/invalidArgs.kt");
+ }
+
+ @TestMetadata("javaStringFormat.kt")
+ public void testJavaStringFormat() throws Exception {
+ runTest("idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat.kt");
+ }
+
+ @TestMetadata("javaStringFormat2.kt")
+ public void testJavaStringFormat2() throws Exception {
+ runTest("idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/javaStringFormat2.kt");
+ }
+
+ @TestMetadata("noArgs.kt")
+ public void testNoArgs() throws Exception {
+ runTest("idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/noArgs.kt");
+ }
+
+ @TestMetadata("notStringPlaceFolder.kt")
+ public void testNotStringPlaceFolder() throws Exception {
+ runTest("idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/notStringPlaceFolder.kt");
+ }
+
+ @TestMetadata("notStringPlaceFolder2.kt")
+ public void testNotStringPlaceFolder2() throws Exception {
+ runTest("idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/notStringPlaceFolder2.kt");
+ }
+
+ @TestMetadata("rawStringFormat.kt")
+ public void testRawStringFormat() throws Exception {
+ runTest("idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/rawStringFormat.kt");
+ }
+
+ @TestMetadata("simple.kt")
+ public void testSimple() throws Exception {
+ runTest("idea/testData/inspectionsLocal/replaceStringFormatWithLiteral/simple.kt");
+ }
+ }
+
@TestMetadata("idea/testData/inspectionsLocal/replaceToWithInfixForm")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)