diff --git a/python/testData/inspections/PyStringFormatInspection/Basic.py b/python/testData/inspections/PyStringFormatInspection/Basic.py
new file mode 100644
index 000000000000..368d82fb4bd5
--- /dev/null
+++ b/python/testData/inspections/PyStringFormatInspection/Basic.py
@@ -0,0 +1,127 @@
+'#%(language)s has %(#)03d quote types.' % {'language': "Python", "#": 2} #ok
+'%d %s' % 5 #Too few arguments for format string
+'Hello world' % 25 #Too many arguments for format string
+"%(name)f(name)" % {'name': 23.2} #ok
+"%()s" % {'': "name"} #ok
+'test%(name)' % {'name': 23} #There are no format specifier character
+'work%*d' % (2, 34) #ok
+'work%(name)*d' % (12, 32) #Can't use '*' in formats when using a mapping
+'%*.*d' % (2, 5, 5) #ok
+'%*.*d' % (2, 4) #Too few arguments for format string
+'%*.*d' % (2, 4, 5, 6) #Too many arguments for format string
+'%**d' % (2, 5) #There are no format specifier character
+'%(name1)s %(name2)s (name3) %s' % {'name1': 'a', 'name2': 'b', 'name3': 'c'} #Too few mapping keys
+'%(name1s' % {'name1': 'a'} #Too few mapping keys
+'%%%(name)ld' % {'name': 12} #ok
+"%(name)f(name)" % 23.2 #Format requires a mapping
+"%(name)f(name)" % (23.2) #Format requires a mapping
+'%d%d' % {'name1': 2, 'name2': 3} #Format doesn't require a mapping
+'%12.2f' % 2.74 #ok
+'Hello world' % () #ok
+'Hello world' % [] #ok
+'Hello world' % {} #ok
+'%d%d' % ((5), (5)) #ok
+"%(name)d %(name)d" % {"name": 43} #ok
+"%(name)d" % {'a': 4, "name": 5} #ok
+'%% name %(name)c' % {'a': 4} #Key 'name' has no following argument
+'%d %u %f %F %s %r' % (2, 3, 4.1, 4.0, "name", "str") #ok
+'%d %d %d' % (4, "a", "b") #Unexpected type
+'%f %f %f' % (4, 5, "test") #Unexpected type
+'%d' % "name" #Unexpected type
+m = {'language': "Python", "#": 2}
+'#%(language)s has %(#)03d quote types.' % m #ok
+i = "test"
+'%(name)s' % {'name': i} #ok
+'%s' % i #ok
+'%f' % i #Unexpected type
+'%f' % (2 * 3 + 5) #ok
+s = "%s" % "a".upper() #ok
+x = ['a', 'b', 'c']
+print "%d: %s" % (len(x), ", ".join(x)) #ok
+m = [1, 2, 3, 4, 5]
+"%d" % m[0] #ok
+"%d %s" % (m[0], m[4]) #ok
+"%s" % m #ok
+"%s" % m[1:3] #ok
+"%d" % m[1:2] #ok
+"%d" % m #Unexpected type
+"%d" % [] #Unexpected type
+def greet(all):
+ print "Hello %s" % ("World" if all else "Human") #ok
+"%s" % [x + 1 for x in [1, 2, 3, 4]] #ok
+"%s" % [x + y for x in []] #ok
+"%s" % [] #ok
+"%f" % [x + 1 for x in [1, 2, 3, 4]] #Unexpected type
+"%d %d" % (3, 5) #ok
+"Hello %s %s" % tuple(['world', '!']) #ok
+
+def foo(a):
+ if a == 1:
+ return "a", "b"
+ else:
+ return "c", "d"
+print "%s" % foo(1) #Too many arguments for format string
+
+print("| [%(issue_id)s|http://youtrack.jetbrains.net/issue/%(issue_id)s] (%(issue_type)s)|%(summary)s|" % (issue_id, issue_type, summary)) #Format requires a mapping (PY-704)
+
+my_list = list()
+for i in range(0,3):
+ my_list.append( ("hey", "you") )
+
+for item in my_list:
+ print '%s %s' % item # ok (PY-734)
+
+def bar():
+ return None
+"%s %s" % bar() #Too few arguments for format string
+
+"%s" % {} # ok; str() works
+"%s" % {'a': 1, 'b': 2} # ok, no names in template and arg counts don't match
+"%s" % object() # ok, str() works
+"foo" % {'bar':1, 'baz':2} # ok: empty template that could use names
+
+a = ('a', 1) if 1 else ('b', 2)
+"%s is %d" % a # ok, must infer unified tuple type
+#PY-3064, because original type of a is tuple, not list
+a = (1,2,3)
+print '%d:%d' % a[:2]
+print '%d:%d' % a[1:2]
+
+string = "qwerty"
+print '%d:%d' % string[:2]
+print '%s:%s' % string[:2]
+print '%s' % string[:2]
+print '%d' % string[:2]
+
+my_tuple = (1,2,3,4,5,6,7,8)
+print '%d, %d' % my_tuple[:7:3]
+print '%d, %d, %d' % my_tuple[:7:3]
+print '%d, %d, %d, %d' % my_tuple[:7:3]
+
+# PY-12801
+print '%d %s' % ((42,) + ('spam',))
+print '%d %s' % (('ham',) + ('spam',))
+print '%d %s' % ((42,) + ())
+print '%d' % ((42,) + ('spam',))
+
+# PY-11274
+import collections
+print '%(foo)s' % collections.OrderedDict(foo=None)
+
+class MyDict(collections.Mapping):
+ def __getitem__(self, key):
+ return 'spam'
+
+ def __iter__(self):
+ yield 'spam'
+
+ def __len__(self):
+ return 1
+
+print '%(foo)s' % MyDict()
+
+foo = {1, 2, 3}
+print('%s %s %s' % foo)
+
+'%s %s %s' % (x for x in range(10))
+
diff --git a/python/testData/inspections/PyStringFormatInspection/DictionaryArgument.py b/python/testData/inspections/PyStringFormatInspection/DictionaryArgument.py
new file mode 100644
index 000000000000..57afe539b09f
--- /dev/null
+++ b/python/testData/inspections/PyStringFormatInspection/DictionaryArgument.py
@@ -0,0 +1,9 @@
+my_dict = {'class': 3}
+
+my_dict['css_class'] = ""
+if my_dict['class']:
+ my_dict['css_class'] = 'class %(class)s' % my_dict
+
+my_dict['tmp'] = 'classes %(css_class)s' % my_dict
+
+my_dict['tmp'] = 'classes %(claz)s' % my_dict
diff --git a/python/testData/inspections/PyStringFormatInspectionSlice/test.py b/python/testData/inspections/PyStringFormatInspection/Slice.py
similarity index 93%
rename from python/testData/inspections/PyStringFormatInspectionSlice/test.py
rename to python/testData/inspections/PyStringFormatInspection/Slice.py
index 8ef36b1ed699..a179d40db68e 100644
--- a/python/testData/inspections/PyStringFormatInspectionSlice/test.py
+++ b/python/testData/inspections/PyStringFormatInspection/Slice.py
@@ -1,5 +1,5 @@
def foo(x):
- return x
+ return x
artist = foo(1)
print('%s' % (artist.lower()[0:10]))
diff --git a/python/testData/inspections/PyStringFormatInspection/TupleMultiplication.py b/python/testData/inspections/PyStringFormatInspection/TupleMultiplication.py
new file mode 100644
index 000000000000..616ea22ef9d5
--- /dev/null
+++ b/python/testData/inspections/PyStringFormatInspection/TupleMultiplication.py
@@ -0,0 +1,5 @@
+argument_pattern = re.compile(r'(%s)\s*(\(\s*(%s)\s*\)\s*)?$'
+ % ((states.Inliner.simplename,) * 2))
+
+t, num = ('foo',), 2
+res = '%d %d' % (t * num)
diff --git a/python/testData/inspections/PyStringFormatInspection/expected.xml b/python/testData/inspections/PyStringFormatInspection/expected.xml
deleted file mode 100644
index c9e8b5f55532..000000000000
--- a/python/testData/inspections/PyStringFormatInspection/expected.xml
+++ /dev/null
@@ -1,189 +0,0 @@
-
-
-
- string-format.py
- 2
- Too few arguments for format string
-
-
- string-format.py
- 3
- Too many arguments for format string
-
-
- string-format.py
- 6
- Format specifier character missing
-
-
- string-format.py
- 8
- Can't use '*' in formats when using a mapping
-
-
- string-format.py
- 10
- Too few arguments for format string
-
-
- string-format.py
- 11
- Too many arguments for format string
-
-
- string-format.py
- 12
- Format specifier character missing
-
-
- string-format.py
- 13
- Too few mapping keys
-
-
- string-format.py
- 14
- Too few mapping keys
-
-
- string-format.py
- 16
- Format requires a mapping
-
-
- string-format.py
- 17
- Format requires a mapping
-
-
- string-format.py
- 18
- Format doesn't require a mapping
-
-
- string-format.py
- 26
- Key 'name' has no following argument
-
-
- string-format.py
- 28
- Unexpected type
-
-
- string-format.py
- 28
- Unexpected type
-
-
- string-format.py
- 29
- Unexpected type
-
-
- string-format.py
- 30
- Unexpected type
-
-
- string-format.py
- 36
- Unexpected type
-
-
- string-format.py
- 46
- Unexpected type
-
-
- string-format.py
- 47
- Unexpected type
-
-
- string-format.py
- 48
- Unexpected type
-
-
- string-format.py
- 54
- Unexpected type
-
-
- string-format.py
- 63
- Too many arguments for format string
-
-
- string-format.py
- 65
- Format requires a mapping
-
-
- string-format.py
- 76
- Too few arguments for format string
-
-
- string-format.py
- 88
- Too few arguments for format string
-
-
- string-format.py
- 91
- Too few arguments for format string
-
-
- string-format.py
- 91
- Unexpected type
-
-
- string-format.py
- 92
- Too few arguments for format string
-
-
- string-format.py
- 94
- Unexpected type
-
-
- string-format.py
- 97
- Too many arguments for format string
-
-
- string-format.py
- 99
- Too few arguments for format string
-
-
- string-format.py
- 103
- Unexpected type
-
-
- string-format.py
- 104
- Too few arguments for format string
-
-
- string-format.py
- 105
- Too many arguments for format string
-
-
- string-format.py
- 124
- Too few arguments for format string
-
-
- string-format.py
- 126
- Too few arguments for format string
-
-
-
diff --git a/python/testData/inspections/PyStringFormatInspection/src/string-format.py b/python/testData/inspections/PyStringFormatInspection/src/string-format.py
deleted file mode 100644
index 16bfe73814a1..000000000000
--- a/python/testData/inspections/PyStringFormatInspection/src/string-format.py
+++ /dev/null
@@ -1,126 +0,0 @@
-'#%(language)s has %(#)03d quote types.' % {'language': "Python", "#": 2} #ok
-'%d %s' % 5 #Too few arguments for format string
-'Hello world' % 25 #Too many arguments for format string
-"%(name)f(name)" % {'name': 23.2} #ok
-"%()s" % {'': "name"} #ok
-'test%(name)' % {'name': 23} #There are no format specifier character
-'work%*d' % (2, 34) #ok
-'work%(name)*d' % (12, 32) #Can't use '*' in formats when using a mapping
-'%*.*d' % (2, 5, 5) #ok
-'%*.*d' % (2, 4) #Too few arguments for format string
-'%*.*d' % (2, 4, 5, 6) #Too many arguments for format string
-'%**d' % (2, 5) #There are no format specifier character
-'%(name1)s %(name2)s (name3) %s' % {'name1': 'a', 'name2': 'b', 'name3': 'c'} #Too few mapping keys
-'%(name1s' % {'name1': 'a'} #Too few mapping keys
-'%%%(name)ld' % {'name': 12} #ok
-"%(name)f(name)" % 23.2 #Format requires a mapping
-"%(name)f(name)" % (23.2) #Format requires a mapping
-'%d%d' % {'name1': 2, 'name2': 3} #Format doesn't require a mapping
-'%12.2f' % 2.74 #ok
-'Hello world' % () #ok
-'Hello world' % [] #ok
-'Hello world' % {} #ok
-'%d%d' % ((5), (5)) #ok
-"%(name)d %(name)d" % {"name": 43} #ok
-"%(name)d" % {'a': 4, "name": 5} #ok
-'%% name %(name)c' % {'a': 4} #Key 'name' has no following argument
-'%d %u %f %F %s %r' % (2, 3, 4.1, 4.0, "name", "str") #ok
-'%d %d %d' % (4, "a", "b") #Unexpected type
-'%f %f %f' % (4, 5, "test") #Unexpected type
-'%d' % "name" #Unexpected type
-m = {'language': "Python", "#": 2}
-'#%(language)s has %(#)03d quote types.' % m #ok
-i = "test"
-'%(name)s' % {'name': i} #ok
-'%s' % i #ok
-'%f' % i #Unexpected type
-'%f' % (2 * 3 + 5) #ok
-s = "%s" % "a".upper() #ok
-x = ['a', 'b', 'c']
-print "%d: %s" % (len(x), ", ".join(x)) #ok
-m = [1, 2, 3, 4, 5]
-"%d" % m[0] #ok
-"%d %s" % (m[0], m[4]) #ok
-"%s" % m #ok
-"%s" % m[1:3] #ok
-"%d" % m[1:2] #ok
-"%d" % m #Unexpected type
-"%d" % [] #Unexpected type
-def greet(all):
- print "Hello %s" % ("World" if all else "Human") #ok
-"%s" % [x + 1 for x in [1, 2, 3, 4]] #ok
-"%s" % [x + y for x in []] #ok
-"%s" % [] #ok
-"%f" % [x + 1 for x in [1, 2, 3, 4]] #Unexpected type
-"%d %d" % (3, 5) #ok
-"Hello %s %s" % tuple(['world', '!']) #ok
-
-def foo(a):
- if a == 1:
- return "a", "b"
- else:
- return "c", "d"
-print "%s" % foo(1) #Too many arguments for format string
-
-print("| [%(issue_id)s|http://youtrack.jetbrains.net/issue/%(issue_id)s] (%(issue_type)s)|%(summary)s|" % (issue_id, issue_type, summary)) #Format requires a mapping (PY-704)
-
-my_list = list()
-for i in range(0,3):
- my_list.append( ("hey", "you") )
-
-for item in my_list:
- print '%s %s' % item # ok (PY-734)
-
-def bar():
- return None
-"%s %s" % bar() #Too few arguments for format string
-
-"%s" % {} # ok; str() works
-"%s" % {'a': 1, 'b': 2} # ok, no names in template and arg counts don't match
-"%s" % object() # ok, str() works
-"foo" % {'bar':1, 'baz':2} # ok: empty template that could use names
-
-a = ('a', 1) if 1 else ('b', 2)
-"%s is %d" % a # ok, must infer unified tuple type
-#PY-3064, because original type of a is tuple, not list
-a = (1,2,3)
-print '%d:%d' % a[:2]
-print '%d:%d' % a[1:2]
-
-string = "qwerty"
-print '%d:%d' % string[:2]
-print '%s:%s' % string[:2]
-print '%s' % string[:2]
-print '%d' % string[:2]
-
-my_tuple = (1,2,3,4,5,6,7,8)
-print '%d, %d' % my_tuple[:7:3]
-print '%d, %d, %d' % my_tuple[:7:3]
-print '%d, %d, %d, %d' % my_tuple[:7:3]
-
-# PY-12801
-print '%d %s' % ((42,) + ('spam',))
-print '%d %s' % (('ham',) + ('spam',))
-print '%d %s' % ((42,) + ())
-print '%d' % ((42,) + ('spam',))
-
-# PY-11274
-import collections
-print '%(foo)s' % collections.OrderedDict(foo=None)
-
-class MyDict(collections.Mapping):
- def __getitem__(self, key):
- return 'spam'
-
- def __iter__(self):
- yield 'spam'
-
- def __len__(self):
- return 1
-
-print '%(foo)s' % MyDict()
-
-foo = {1, 2, 3}
-print('%s %s %s' % foo)
-
-'%s %s %s' % (x for x in range(10))
diff --git a/python/testData/inspections/PyStringFormatInspection1/test.py b/python/testData/inspections/PyStringFormatInspection1/test.py
deleted file mode 100644
index 92212324a230..000000000000
--- a/python/testData/inspections/PyStringFormatInspection1/test.py
+++ /dev/null
@@ -1,16 +0,0 @@
-my_dict = {'class': 3}
-
-my_dict['css_class'] = ""
-if my_dict['class']:
- my_dict['css_class'] = 'class %(class)s' % my_dict
-
-my_dict['tmp'] = 'classes %(css_class)s' % my_dict
-
-my_dict['tmp'] = 'classes %(claz)s' % my_dict
-
-#PY-4647
-argument_pattern = re.compile(r'(%s)\s*(\(\s*(%s)\s*\)\s*)?$'
- % ((states.Inliner.simplename,) * 2))
-
-t, num = ('foo',), 2
-res = '%d %d' % (t * num)
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java
index b85751460054..721d76be818b 100644
--- a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java
+++ b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java
@@ -70,11 +70,6 @@ public class PythonInspectionsTest extends PyTestCase {
doHighlightingTest(PyRedeclarationInspection.class);
}
- public void testPyStringFormatInspection() {
- LocalInspectionTool inspection = new PyStringFormatInspection();
- doTest(getTestName(false), inspection);
- }
-
public void testPyTrailingSemicolonInspection() {
LocalInspectionTool inspection = new PyTrailingSemicolonInspection();
doTest(getTestName(false), inspection);
@@ -287,14 +282,6 @@ public class PythonInspectionsTest extends PyTestCase {
doHighlightingTest(PyListCreationInspection.class);
}
- public void testPyStringFormatInspection1() { //PY-2836
- doHighlightingTest(PyStringFormatInspection.class);
- }
-
- public void testPyStringFormatInspectionSlice() { //PY-6756
- doHighlightingTest(PyStringFormatInspection.class);
- }
-
public void testPyUnnecessaryBackslashInspection() { //PY-2952
setLanguageLevel(LanguageLevel.PYTHON27);
doHighlightingTest(PyUnnecessaryBackslashInspection.class);
diff --git a/python/testSrc/com/jetbrains/python/inspections/PyStringFormatInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyStringFormatInspectionTest.java
new file mode 100644
index 000000000000..af787e153a46
--- /dev/null
+++ b/python/testSrc/com/jetbrains/python/inspections/PyStringFormatInspectionTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2000-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.inspections;
+
+import com.jetbrains.python.fixtures.PyTestCase;
+
+/**
+ * @author vlan
+ */
+public class PyStringFormatInspectionTest extends PyTestCase {
+ public static final String TEST_DIRECTORY = "inspections/PyStringFormatInspection/";
+
+ public void testBasic() {
+ doTest();
+ }
+
+ // PY-2836
+ public void testDictionaryArgument() {
+ doTest();
+ }
+
+ // PY-4647
+ public void testTupleMultiplication() {
+ doTest();
+ }
+
+ // PY-6756
+ public void testSlice() {
+ doTest();
+ }
+
+ private void doTest() {
+ myFixture.configureByFile(TEST_DIRECTORY + getTestName(false) + ".py");
+ myFixture.enableInspections(PyStringFormatInspection.class);
+ myFixture.checkHighlighting(true, false, true);
+ }
+}