diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index ccb551a65f62..f9220330514b 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -78,6 +78,9 @@ QFIX.unresolved.reference.replace.$0=Replace with {0} #PyDefaultArgumentQuickFix QFIX.default.argument=Replace mutable default argument +#RemoveArgumentEqualDefaultQuickFix +QFIX.remove.argument.equal.default=Remove argument equal to default + # 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' @@ -360,6 +363,9 @@ INSP.NAME.augment.assignment=Assignment can be replaced with augmented assignmen # PyChainedComparsonsInspection INSP.NAME.chained.comparisons=Chained comparisons can be simplified +# PyArgumentEqualDefaultInspection +INSP.NAME.argument.equal.default=Argument passed to function is equal to default parameter value + # PyOldStyleClassesInspection INSP.NAME.oldstyle.class=Old-style class contains new-style class features diff --git a/python/src/com/jetbrains/python/actions/RemoveArgumentEqualDefaultQuickFix.java b/python/src/com/jetbrains/python/actions/RemoveArgumentEqualDefaultQuickFix.java new file mode 100644 index 000000000000..a393d6df8c86 --- /dev/null +++ b/python/src/com/jetbrains/python/actions/RemoveArgumentEqualDefaultQuickFix.java @@ -0,0 +1,30 @@ +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 org.jetbrains.annotations.NotNull; + +/** + * User: catherine + * + * QuickFix to remove redundant argument equal default + */ +public class RemoveArgumentEqualDefaultQuickFix implements LocalQuickFix { + @NotNull + public String getName() { + return PyBundle.message("QFIX.remove.argument.equal.default"); + } + + @NotNull + public String getFamilyName() { + return PyBundle.message("INSP.GROUP.python"); + } + + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiElement element = descriptor.getPsiElement(); + element.delete(); + } +} diff --git a/python/src/com/jetbrains/python/inspections/PyArgumentEqualDefaultInspection.java b/python/src/com/jetbrains/python/inspections/PyArgumentEqualDefaultInspection.java new file mode 100644 index 000000000000..b9c0cf4a3cdd --- /dev/null +++ b/python/src/com/jetbrains/python/inspections/PyArgumentEqualDefaultInspection.java @@ -0,0 +1,77 @@ +package com.jetbrains.python.inspections; + +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiReference; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.actions.RemoveArgumentEqualDefaultQuickFix; +import com.jetbrains.python.psi.*; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +/** + * User: catherine + * + * Inspection to detect situations, where argument passed to function + * is equal to default parameter value + * for instance, + * dict().get(x, None) --> None is default value for second param in dict().get function + */ +public class PyArgumentEqualDefaultInspection extends PyInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return PyBundle.message("INSP.NAME.argument.equal.default"); + } + + @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){ + PyExpression[] arguments = node.getArguments(); + PyExpression callee = node.getCallee(); + if (callee != null) { + PsiReference ref = callee.getReference(); + if (ref != null) { + PsiElement function = ref.resolve(); + if (function instanceof PyFunction) { + checkArguments(function, arguments); + } + } + } + } + + private void checkArguments(PsiElement function, PyExpression[] arguments) { + int argumentsSize = arguments.length; + PyClass containingClass = ((PyFunction)function).getContainingClass(); + int adjust = 0; + if (containingClass != null) + adjust = 1; + PyParameter[] params = ((PyFunction)function).getParameterList().getParameters(); + for (int i = 0; i != params.length - adjust; ++i) { + PyParameter p = params[i+adjust]; + if (p instanceof PyNamedParameter) { + PyExpression defaultValue = p.getDefaultValue(); + if (defaultValue != null && i < argumentsSize) { + if (arguments[i].getText().equals(defaultValue.getText())) { + registerProblem(arguments[i], "Argument equals to default parameter value", + new RemoveArgumentEqualDefaultQuickFix()); + } + } + } + } + } + } +} diff --git a/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java b/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java index 7f619eb2d23c..e0b81516eea2 100644 --- a/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java +++ b/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java @@ -57,6 +57,7 @@ public class PythonInspectionToolProvider implements InspectionToolProvider { PyUnnecessaryBackslashInspection.class, PySingleQuotedDocstringInspection.class, PyMissingConstructorInspection.class, + PyArgumentEqualDefaultInspection.class, }; } } diff --git a/python/testData/inspections/ArgumentEqualDefault.py b/python/testData/inspections/ArgumentEqualDefault.py new file mode 100644 index 000000000000..964d0bcb7ffd --- /dev/null +++ b/python/testData/inspections/ArgumentEqualDefault.py @@ -0,0 +1,4 @@ +def foo(a, b = 345, c = 1): + pass + +foo(1, 345, 22) \ No newline at end of file diff --git a/python/testData/inspections/ArgumentEqualDefault_after.py b/python/testData/inspections/ArgumentEqualDefault_after.py new file mode 100644 index 000000000000..00e31ef94b4c --- /dev/null +++ b/python/testData/inspections/ArgumentEqualDefault_after.py @@ -0,0 +1,4 @@ +def foo(a, b = 345, c = 1): + pass + +foo(1, 22) \ No newline at end of file diff --git a/python/testData/inspections/PyArgumentEqualDefaultInspection/test.py b/python/testData/inspections/PyArgumentEqualDefaultInspection/test.py new file mode 100644 index 000000000000..f802999c9021 --- /dev/null +++ b/python/testData/inspections/PyArgumentEqualDefaultInspection/test.py @@ -0,0 +1,7 @@ +def foo(a, b = 345, c = 1): + pass + +foo(1, 345, 22) + +a = dict() +a.get(1, None) \ 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 652f5b5883bf..bb0c7c71c2c0 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -235,6 +235,11 @@ public class PyQuickFixTest extends PyLightFixtureTestCase { PyBundle.message("QFIX.default.argument"), true, true); } + public void testPyArgumentEqualDefault() { //PY-3125 + doInspectionTest("ArgumentEqualDefault.py", PyArgumentEqualDefaultInspection.class, + PyBundle.message("QFIX.remove.argument.equal.default"), 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 9810a9cd7f2a..004bc57a4634 100644 --- a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java +++ b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java @@ -322,4 +322,8 @@ public class PythonInspectionsTest extends PyLightFixtureTestCase { setLanguageLevel(LanguageLevel.PYTHON30); doHighlightingTest(PyMissingConstructorInspection.class); } + + public void testPyArgumentEqualDefaultInspection() { //PY-3125 + doHighlightingTest(PyArgumentEqualDefaultInspection.class); + } }