diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties
index f9220330514b..7b0f4fe39db2 100644
--- a/python/src/com/jetbrains/python/PyBundle.properties
+++ b/python/src/com/jetbrains/python/PyBundle.properties
@@ -81,6 +81,9 @@ QFIX.default.argument=Replace mutable default argument
#RemoveArgumentEqualDefaultQuickFix
QFIX.remove.argument.equal.default=Remove argument equal to default
+# ReplaceFunctionWithSetLiteralQuickFix
+QFIX.replace.function.set.with.literal=Replace function call with set literal
+
# Intentions: INTN
INTN.Family.convert.import.unqualify=Convert 'import module' to 'from module import'
INTN.Family.convert.import.qualify=Convert 'from module import' to 'import module'
@@ -381,6 +384,9 @@ INSP.NAME.single.quoted.docstring=Single quoted docstring
# PyMissingConstructorInspection
INSP.NAME.missing.super.constructor=Missed call to constructor of super class
+# PySetFunctionToLiteralInspection
+INSP.NAME.set.function.to.literal=Function call can be replaced with set literal
+
# Refactoring
# introduce
refactoring.introduce.name.error=Incorrect name
diff --git a/python/src/com/jetbrains/python/actions/ReplaceFunctionWithSetLiteralQuickFix.java b/python/src/com/jetbrains/python/actions/ReplaceFunctionWithSetLiteralQuickFix.java
new file mode 100644
index 000000000000..d94e14af2da9
--- /dev/null
+++ b/python/src/com/jetbrains/python/actions/ReplaceFunctionWithSetLiteralQuickFix.java
@@ -0,0 +1,58 @@
+package com.jetbrains.python.actions;
+
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.psi.*;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * User : catherine
+ * Quick Fix to replace function call of built-in function "set" with
+ * set literal if applicable
+ */
+public class ReplaceFunctionWithSetLiteralQuickFix implements LocalQuickFix {
+ @Override
+ @NotNull
+ public String getName() {
+ return PyBundle.message("QFIX.replace.function.set.with.literal");
+ }
+
+ @Override
+ @NotNull
+ public String getFamilyName() {
+ return PyBundle.message("INSP.GROUP.python");
+ }
+
+ @Override
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project);
+ PsiElement functionCall = descriptor.getPsiElement();
+ StringBuilder str = new StringBuilder("{");
+ if (functionCall instanceof PyCallExpression) {
+ PyExpression[] arguments = ((PyCallExpression)functionCall).getArguments();
+ if (arguments.length > 0) {
+ PyExpression argument= arguments[0];
+ PyElement[] elements = {};
+ if (argument instanceof PySequenceExpression)
+ elements = ((PySequenceExpression)argument).getElements();
+ if (argument instanceof PyParenthesizedExpression) {
+ PyExpression tuple = ((PyParenthesizedExpression)argument).getContainedExpression();
+ if (tuple instanceof PyTupleExpression)
+ elements = ((PyTupleExpression)(tuple)).getElements();
+ }
+ for (int i = 0; i != elements.length; ++i) {
+ PyElement e = elements[i];
+ str.append(e.getText());
+ if (i != elements.length-1)
+ str.append(", ");
+ }
+ }
+ str.append("}");
+ functionCall.replace(elementGenerator.createFromText(LanguageLevel.forElement(functionCall), PyExpressionStatement.class,
+ str.toString()).getExpression());
+ }
+ }
+}
diff --git a/python/src/com/jetbrains/python/inspections/PySetFunctionToLiteralInspection.java b/python/src/com/jetbrains/python/inspections/PySetFunctionToLiteralInspection.java
new file mode 100644
index 000000000000..2b86ee336282
--- /dev/null
+++ b/python/src/com/jetbrains/python/inspections/PySetFunctionToLiteralInspection.java
@@ -0,0 +1,78 @@
+package com.jetbrains.python.inspections;
+
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.openapi.roots.ProjectFileIndex;
+import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiReference;
+import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.actions.ReplaceFunctionWithSetLiteralQuickFix;
+import com.jetbrains.python.psi.*;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * User: catherine
+ *
+ * Inspection to find set built-in function and replace it with set literal
+ * available if the selected language level supports set literals.
+ */
+public class PySetFunctionToLiteralInspection extends PyInspection {
+
+ @Nls
+ @NotNull
+ @Override
+ public String getDisplayName() {
+ return PyBundle.message("INSP.NAME.set.function.to.literal");
+ }
+
+ @NotNull
+ @Override
+ public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
+ return new Visitor(holder);
+ }
+
+ private static class Visitor extends PyInspectionVisitor {
+ public Visitor(final ProblemsHolder holder) {
+ super(holder);
+ }
+
+ @Override
+ public void visitPyCallExpression(final PyCallExpression node) {
+ if (LanguageLevel.forElement(node).supportsSetLiterals()) {
+ PyExpression callee = node.getCallee();
+ if (callee != null) {
+ if (isBuiltinSet(callee)) {
+ PyExpression[] arguments = node.getArguments();
+ if (arguments.length == 0 ||(arguments.length == 1) &&
+ (arguments[0] instanceof PySequenceExpression ||
+ (arguments[0] instanceof PyParenthesizedExpression &&
+ ((PyParenthesizedExpression)arguments[0]).getContainedExpression() instanceof PyTupleExpression)))
+ registerProblem(node, "Function call can be replaced with set literal",
+ new ReplaceFunctionWithSetLiteralQuickFix());
+ }
+ }
+ }
+ }
+
+ private static boolean isBuiltinSet(PyExpression callee) {
+ ProjectFileIndex ind = ProjectRootManager.getInstance(callee.getProject()).getFileIndex();
+ PsiReference reference = callee.getReference();
+ if (reference != null) {
+ PsiElement resolved = reference.resolve();
+ if (resolved != null) {
+ PsiFile file = resolved.getContainingFile();
+ if (file != null && file.getVirtualFile() != null && ind.isInLibraryClasses(file.getVirtualFile())) {
+ if (callee.getText().equals("set")) {
+ return true;
+
+ }
+ }
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java b/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java
index e0b81516eea2..bc152a531797 100644
--- a/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java
+++ b/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java
@@ -58,6 +58,7 @@ public class PythonInspectionToolProvider implements InspectionToolProvider {
PySingleQuotedDocstringInspection.class,
PyMissingConstructorInspection.class,
PyArgumentEqualDefaultInspection.class,
+ PySetFunctionToLiteralInspection.class,
};
}
}
diff --git a/python/testData/inspections/PySetFunctionToLiteralInspection/test.py b/python/testData/inspections/PySetFunctionToLiteralInspection/test.py
new file mode 100644
index 000000000000..0618bde35be8
--- /dev/null
+++ b/python/testData/inspections/PySetFunctionToLiteralInspection/test.py
@@ -0,0 +1,11 @@
+my_set = set()
+my_set = set([1,2,3])
+my_set = set((1,2,3))
+
+my_set = set(var)
+
+def set(fake=None):
+ pass
+
+my_fake_set = set()
+my_fake_set = set([1,2,3])
diff --git a/python/testData/inspections/SetFunctionToLiteral.py b/python/testData/inspections/SetFunctionToLiteral.py
new file mode 100644
index 000000000000..5e6bf82ba5ed
--- /dev/null
+++ b/python/testData/inspections/SetFunctionToLiteral.py
@@ -0,0 +1 @@
+my_set = set([1,2,3])
\ No newline at end of file
diff --git a/python/testData/inspections/SetFunctionToLiteral_after.py b/python/testData/inspections/SetFunctionToLiteral_after.py
new file mode 100644
index 000000000000..94a51c2963f9
--- /dev/null
+++ b/python/testData/inspections/SetFunctionToLiteral_after.py
@@ -0,0 +1 @@
+my_set = {1, 2, 3}
\ No newline at end of file
diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java
index 60240a70ae7d..b4d71a5fc108 100644
--- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java
+++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java
@@ -240,6 +240,12 @@ public class PyQuickFixTest extends PyLightFixtureTestCase {
PyBundle.message("QFIX.remove.argument.equal.default"), true, true);
}
+ public void testSetFunctionToLiteral() { //PY-3120
+ setLanguageLevel(LanguageLevel.PYTHON27);
+ doInspectionTest("SetFunctionToLiteral.py", PySetFunctionToLiteralInspection.class,
+ PyBundle.message("QFIX.replace.function.set.with.literal"), true, true);
+ }
+
public void testUnnecessaryBackslash() {
String[] testFiles = new String[]{"UnnecessaryBackslash.py"};
myFixture.enableInspections(PyUnnecessaryBackslashInspection.class);
diff --git a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java
index 004bc57a4634..f8d505b5fb98 100644
--- a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java
+++ b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java
@@ -326,4 +326,9 @@ public class PythonInspectionsTest extends PyLightFixtureTestCase {
public void testPyArgumentEqualDefaultInspection() { //PY-3125
doHighlightingTest(PyArgumentEqualDefaultInspection.class);
}
+
+ public void testPySetFunctionToLiteralInspection() { //PY-3120
+ setLanguageLevel(LanguageLevel.PYTHON27);
+ doHighlightingTest(PySetFunctionToLiteralInspection.class);
+ }
}