PY-85080 Add quick-fix to remove unused Type Parameters

GitOrigin-RevId: cbb9b8136d07350075a9af8b4bc5aaa8891edd73
This commit is contained in:
Daniil Kalinin
2025-10-23 22:48:43 +00:00
committed by intellij-monorepo-bot
parent 931cf5c4e1
commit 6bfbd40f78
8 changed files with 77 additions and 1 deletions
@@ -374,6 +374,7 @@ QFIX.NAME.add.field.to.class=Add field to class
QFIX.add.field.to.class=Add field ''{0}'' to class {1}
QFIX.add.field.to.class.popup.content.added.init=Added a __init__ to class <code>{0}</code><br/>to accommodate new field <code>{1}</code>
QFIX.NAME.remove.parameter=Remove parameter
QFIX.NAME.remove.type.parameter=Remove type parameter
QFIX.NAME.rename.parameter=Rename parameter
QFIX.rename.parameter=Rename to ''{0}''
QFIX.NAME.remove.statement=Remove statement
@@ -0,0 +1,49 @@
package com.jetbrains.python.inspections.quickfix
import com.intellij.modcommand.ModPsiUpdater
import com.intellij.modcommand.PsiUpdateModCommandQuickFix
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.util.PsiTreeUtil
import com.jetbrains.python.PyPsiBundle
import com.jetbrains.python.PyTokenTypes
import com.jetbrains.python.psi.PyElement
import com.jetbrains.python.psi.PyTypeParameter
import com.jetbrains.python.psi.PyTypeParameterList
/**
* Removes a single type parameter from a type parameter list.
* If the list becomes empty, removes the brackets as well.
*/
class PyRemoveTypeParameterQuickFix : PsiUpdateModCommandQuickFix() {
override fun getFamilyName(): String = PyPsiBundle.message("QFIX.NAME.remove.type.parameter")
override fun applyFix(project: Project, element: PsiElement, updater: ModPsiUpdater) {
val typeParameter = PsiTreeUtil.getParentOfType(element, PyTypeParameter::class.java, false) ?: return
val list = PsiTreeUtil.getParentOfType(typeParameter, PyTypeParameterList::class.java, false) ?: return
var prev: PsiElement? = typeParameter.prevSibling
var next: PsiElement? = typeParameter.nextSibling
if (prev is PsiWhiteSpace) prev = prev.prevSibling
if (next is PsiWhiteSpace) next = next.nextSibling
if (prev != null && prev.node.elementType == PyTokenTypes.COMMA) {
prev.delete()
val afterComma = typeParameter.prevSibling
if (afterComma is PsiWhiteSpace) afterComma.delete()
}
else if (next != null && next.node.elementType == PyTokenTypes.COMMA) {
val ws = next.nextSibling
next.delete()
if (ws is PsiWhiteSpace) ws.delete()
}
typeParameter.delete()
if (list.typeParameters.isEmpty()) {
(list as PyElement).delete()
}
}
}
@@ -362,7 +362,7 @@ public final class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor {
final PsiElement name = typeParameter.getNameIdentifier();
registerWarning(name != null ? name : element,
PyPsiBundle.message("INSP.unused.locals.type.parameter.isnot.used", typeParameter.getName()),
new PyRemoveStatementQuickFix());
new PyRemoveTypeParameterQuickFix());
}
else {
// Local variable or parameter
@@ -0,0 +1,4 @@
from typing import Callable
def foo[T, <caret>U](fn: Callable[..., T]) -> T:
return fn()
@@ -0,0 +1,4 @@
from typing import Callable
def foo[T](fn: Callable[..., T]) -> T:
return fn()
@@ -0,0 +1,3 @@
from typing import Callable
type Alias[T, <caret>U] = Callable[..., T]
@@ -0,0 +1,3 @@
from typing import Callable
type Alias[T] = Callable[..., T]
@@ -103,4 +103,16 @@ public class PyRemoveUnusedLocalQuickFixTest extends PyQuickFixTestCase {
public void testComprehensionIterator() {
doQuickFixTest(PyUnusedLocalInspection.class, PyPsiBundle.message("INSP.unused.locals.replace.with.wildcard"));
}
// PY-85080
public void testRemoveUnusedTypeParameterInFunction() {
runWithLanguageLevel(LanguageLevel.PYTHON312,
() -> doQuickFixTest(PyUnusedLocalInspection.class, PyPsiBundle.message("QFIX.NAME.remove.type.parameter")));
}
// PY-85080
public void testRemoveUnusedTypeParameterInTypeAlias() {
runWithLanguageLevel(LanguageLevel.PYTHON312,
() -> doQuickFixTest(PyUnusedLocalInspection.class, PyPsiBundle.message("QFIX.NAME.remove.type.parameter")));
}
}