From 6f3dbf345a0c3d57047fb286faa52efe69edfd6c Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Thu, 24 Mar 2011 12:51:26 +0300 Subject: [PATCH] added PY-3120 Inspection to replace set built-in function with set literal --- .../com/jetbrains/python/PyBundle.properties | 6 ++ ...ReplaceFunctionWithSetLiteralQuickFix.java | 58 ++++++++++++++ .../PySetFunctionToLiteralInspection.java | 78 +++++++++++++++++++ .../PythonInspectionToolProvider.java | 1 + .../PySetFunctionToLiteralInspection/test.py | 11 +++ .../inspections/SetFunctionToLiteral.py | 1 + .../inspections/SetFunctionToLiteral_after.py | 1 + .../com/jetbrains/python/PyQuickFixTest.java | 6 ++ .../python/PythonInspectionsTest.java | 5 ++ 9 files changed, 167 insertions(+) create mode 100644 python/src/com/jetbrains/python/actions/ReplaceFunctionWithSetLiteralQuickFix.java create mode 100644 python/src/com/jetbrains/python/inspections/PySetFunctionToLiteralInspection.java create mode 100644 python/testData/inspections/PySetFunctionToLiteralInspection/test.py create mode 100644 python/testData/inspections/SetFunctionToLiteral.py create mode 100644 python/testData/inspections/SetFunctionToLiteral_after.py 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); + } }