diff --git a/python/src/com/jetbrains/python/inspections/PyShadowingBuiltinsInspection.java b/python/src/com/jetbrains/python/inspections/PyShadowingBuiltinsInspection.java
index 3c81974adacf..64c3ff48a5c3 100644
--- a/python/src/com/jetbrains/python/inspections/PyShadowingBuiltinsInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyShadowingBuiltinsInspection.java
@@ -1,10 +1,24 @@
package com.jetbrains.python.inspections;
import com.intellij.codeInspection.LocalInspectionToolSession;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiNameIdentifierOwner;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.refactoring.rename.PsiElementRenameHandler;
+import com.intellij.refactoring.rename.RenameProcessor;
+import com.intellij.refactoring.rename.RenamePsiElementProcessor;
+import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer;
+import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import org.jetbrains.annotations.NotNull;
@@ -62,7 +76,55 @@ public class PyShadowingBuiltinsInspection extends PyInspection {
final PsiElement builtin = builtinCache.getByName(name);
if (builtin != null && !PyUtil.inSameFile(builtin, element)) {
final PsiElement identifier = element.getNameIdentifier();
- registerProblem(identifier != null ? identifier : element, "Shadows a built-in with the same name");
+ registerProblem(identifier != null ? identifier : element, "Shadows a built-in with the same name",
+ new PyRenameElementQuickFix());
+ }
+ }
+ }
+
+ private static class PyRenameElementQuickFix implements LocalQuickFix {
+ @NotNull
+ @Override
+ public String getName() {
+ return "Rename element";
+ }
+
+ @NotNull
+ @Override
+ public String getFamilyName() {
+ return "Rename element";
+ }
+
+ @Override
+ public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
+ final PsiElement element = descriptor.getPsiElement();
+ final PsiNameIdentifierOwner nameOwner = element instanceof PsiNameIdentifierOwner ?
+ (PsiNameIdentifierOwner)element :
+ PsiTreeUtil.getParentOfType(element, PsiNameIdentifierOwner.class, true);
+ if (nameOwner != null) {
+ final VirtualFile virtualFile = nameOwner.getContainingFile().getVirtualFile();
+ if (virtualFile != null) {
+ final Editor editor = FileEditorManager.getInstance(project).openTextEditor(new OpenFileDescriptor(project, virtualFile), true);
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ renameInUnitTestMode(project, nameOwner, editor);
+ }
+ else {
+ if (nameOwner instanceof PyNamedParameter || nameOwner instanceof PyTargetExpression) {
+ new VariableInplaceRenamer(nameOwner, editor).performInplaceRename();
+ }
+ else {
+ PsiElementRenameHandler.invoke(nameOwner, project, ScopeUtil.getScopeOwner(nameOwner), editor);
+ }
+ }
+ }
+ }
+ }
+
+ private static void renameInUnitTestMode(@NotNull Project project, @NotNull PsiNameIdentifierOwner nameOwner,
+ @Nullable Editor editor) {
+ final PsiElement substitution = RenamePsiElementProcessor.forElement(nameOwner).substituteElementToRename(nameOwner, editor);
+ if (substitution != null) {
+ new RenameProcessor(project, substitution, "a", false, false).run();
}
}
}
diff --git a/python/testData/inspections/RenameFunctionShadowingBuiltins.py b/python/testData/inspections/RenameFunctionShadowingBuiltins.py
new file mode 100644
index 000000000000..ed54b1c887d9
--- /dev/null
+++ b/python/testData/inspections/RenameFunctionShadowingBuiltins.py
@@ -0,0 +1,6 @@
+def id(x):
+ return x
+
+
+def f():
+ return id('foo')
diff --git a/python/testData/inspections/RenameFunctionShadowingBuiltins_after.py b/python/testData/inspections/RenameFunctionShadowingBuiltins_after.py
new file mode 100644
index 000000000000..57ac3e896508
--- /dev/null
+++ b/python/testData/inspections/RenameFunctionShadowingBuiltins_after.py
@@ -0,0 +1,6 @@
+def a(x):
+ return x
+
+
+def f():
+ return a('foo')
diff --git a/python/testData/inspections/RenameShadowingBuiltins.py b/python/testData/inspections/RenameShadowingBuiltins.py
new file mode 100644
index 000000000000..f22217991759
--- /dev/null
+++ b/python/testData/inspections/RenameShadowingBuiltins.py
@@ -0,0 +1,3 @@
+def f(name):
+ file = open(name, 'rb')
+ return file.read()
diff --git a/python/testData/inspections/RenameShadowingBuiltins_after.py b/python/testData/inspections/RenameShadowingBuiltins_after.py
new file mode 100644
index 000000000000..4f8354971055
--- /dev/null
+++ b/python/testData/inspections/RenameShadowingBuiltins_after.py
@@ -0,0 +1,3 @@
+def f(name):
+ a = open(name, 'rb')
+ return a.read()
diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java
index 84b60a170548..4a7aba094c2b 100644
--- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java
+++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java
@@ -424,6 +424,19 @@ public class PyQuickFixTest extends PyTestCase {
myFixture.checkResultByFile(graftBeforeExt(testFiles[0], "_after"));
}
+ // PY-8788
+ public void testRenameShadowingBuiltins() {
+ doInspectionTest("RenameShadowingBuiltins.py", PyShadowingBuiltinsInspection.class,
+ "Rename element", true, true);
+ }
+
+ // PY-8788
+ public void testRenameFunctionShadowingBuiltins() {
+ doInspectionTest("RenameFunctionShadowingBuiltins.py", PyShadowingBuiltinsInspection.class,
+ "Rename element", true, true);
+
+ }
+
@Override
@NonNls
protected String getTestDataPath() {