From a31467ba68855d2dc8d030c87d93c893637d10b5 Mon Sep 17 00:00:00 2001 From: UNV Date: Fri, 10 Oct 2025 20:46:44 +0300 Subject: [PATCH 1/2] Localizing inspection, intentions and quick fixes (part 4). --- .../BuildoutUnresolvedPartInspection.java | 127 ++- .../ImportFromToImportIntention.java | 6 +- .../intentions/PyBaseIntentionAction.java | 21 +- .../intentions/PyDemorganIntention.java | 188 ++-- ...DictLiteralFormToConstructorIntention.java | 144 ++-- .../intentions/PyFlipComparisonIntention.java | 125 ++- .../PyGenerateDocstringIntention.java | 175 ++-- .../intentions/PyJoinIfIntention.java | 225 ++--- .../PyNegateComparisonIntention.java | 145 ++-- .../intentions/PyQuotedStringIntention.java | 232 +++-- .../intentions/PySplitIfIntention.java | 191 ++-- ...yStringConcatenationToFormatIntention.java | 313 ++++--- ...ansformConditionalExpressionIntention.java | 109 ++- .../intentions/PyYieldFromIntention.java | 146 ++-- ...laceListComprehensionWithForIntention.java | 210 +++-- .../SpecifyTypeInDocstringIntention.java | 149 ++-- .../SpecifyTypeInPy3AnnotationsIntention.java | 306 +++---- .../intentions/TypeAssertionIntention.java | 2 - .../codeInsight/intentions/TypeIntention.java | 397 ++++----- .../PySingleQuotedDocstringInspection.java | 119 +-- .../PyStatementEffectInspection.java | 249 +++--- .../PyStringExceptionInspection.java | 61 +- .../inspections/PyStringFormatInspection.java | 814 +++++++++--------- .../PySuperArgumentsInspection.java | 108 +-- .../PyTrailingSemicolonInspection.java | 72 +- .../PyTupleAssignmentBalanceInspection.java | 122 +-- .../PyTupleItemAssignmentInspection.java | 76 +- .../inspections/PyTypeCheckerInspection.java | 454 +++++----- .../PyUnboundLocalVariableInspection.java | 318 +++---- .../PyUnnecessaryBackslashInspection.java | 163 ++-- .../PyUnreachableCodeInspection.java | 148 ++-- .../inspections/PyUnusedLocalInspection.java | 83 +- .../quickfix/PyFillParagraphFix.java | 26 +- .../PyUnresolvedReferencesInspection.java | 32 +- .../rest/inspections/RestInspection.java | 59 +- .../rest/inspections/RestRoleInspection.java | 296 +++---- 36 files changed, 3156 insertions(+), 3255 deletions(-) diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/buildout/config/inspection/BuildoutUnresolvedPartInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/buildout/config/inspection/BuildoutUnresolvedPartInspection.java index f7417a5c..5f53d6b1 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/buildout/config/inspection/BuildoutUnresolvedPartInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/buildout/config/inspection/BuildoutUnresolvedPartInspection.java @@ -13,11 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.buildout.config.inspection; import com.google.common.collect.Lists; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.buildout.config.BuildoutCfgFileType; import com.jetbrains.python.impl.buildout.config.psi.impl.BuildoutCfgValueLine; import com.jetbrains.python.impl.buildout.config.ref.BuildoutPartReference; @@ -31,9 +29,10 @@ import consulo.language.psi.PsiFile; import consulo.language.psi.PsiRecursiveElementVisitor; import consulo.language.psi.PsiReference; -import org.jetbrains.annotations.Nls; - +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; + import java.util.List; /** @@ -41,75 +40,75 @@ */ @ExtensionImpl public class BuildoutUnresolvedPartInspection extends LocalInspectionTool { - @Nls - @Nonnull - @Override - public String getGroupDisplayName() { - return PyBundle.message("buildout"); - } - - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("buildout.unresolved.part.inspection"); - } - - @Nonnull - @Override - public String getShortName() { - return "BuildoutUnresolvedPartInspection"; - } - - @Nonnull - @Override - public HighlightDisplayLevel getDefaultLevel() { - return HighlightDisplayLevel.WARNING; - } - - @Override - public boolean isEnabledByDefault() { - return true; - } - - @Override - public ProblemDescriptor[] checkFile(@Nonnull PsiFile file, @Nonnull InspectionManager manager, boolean isOnTheFly) { - List problems = Lists.newArrayList(); - if (file.getFileType().equals(BuildoutCfgFileType.INSTANCE)) { - Visitor visitor = new Visitor(); - file.accept(visitor); + @Nonnull + @Override + public LocalizeValue getGroupDisplayName() { + return PyLocalize.buildout(); + } - for (BuildoutPartReference ref : visitor.getUnresolvedParts()) { - ProblemDescriptor d = manager - .createProblemDescriptor(ref.getElement(), ref.getRangeInElement(), PyBundle.message("buildout.unresolved.part.inspection.msg"), - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false); - problems.add(d); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.buildoutUnresolvedPartInspection(); } - return problems.toArray(new ProblemDescriptor[problems.size()]); - } - private class Visitor extends PsiRecursiveElementVisitor { - private final List unresolvedParts = Lists.newArrayList(); + @Nonnull + @Override + public String getShortName() { + return "BuildoutUnresolvedPartInspection"; + } + @Nonnull @Override - public void visitElement(PsiElement element) { - if (element instanceof BuildoutCfgValueLine) { - PsiReference[] refs = element.getReferences(); - for (PsiReference ref : refs) { - if (ref instanceof BuildoutPartReference && ref.resolve() == null) { - unresolvedParts.add((BuildoutPartReference)ref); - } - } + public HighlightDisplayLevel getDefaultLevel() { + return HighlightDisplayLevel.WARNING; + } - } - super.visitElement(element); //To change body of overridden methods use File | Settings | File Templates. + @Override + public boolean isEnabledByDefault() { + return true; } - public List getUnresolvedParts() { - return unresolvedParts; + @Override + public ProblemDescriptor[] checkFile(@Nonnull PsiFile file, @Nonnull InspectionManager manager, boolean isOnTheFly) { + List problems = Lists.newArrayList(); + if (file.getFileType().equals(BuildoutCfgFileType.INSTANCE)) { + Visitor visitor = new Visitor(); + file.accept(visitor); + + for (BuildoutPartReference ref : visitor.getUnresolvedParts()) { + ProblemDescriptor d = manager.createProblemDescriptor( + ref.getElement(), + ref.getRangeInElement(), + PyLocalize.buildoutUnresolvedPartInspectionMsg().get(), + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + false + ); + problems.add(d); + } + } + return problems.toArray(new ProblemDescriptor[problems.size()]); } - } + private class Visitor extends PsiRecursiveElementVisitor { + private final List unresolvedParts = Lists.newArrayList(); + + @Override + public void visitElement(PsiElement element) { + if (element instanceof BuildoutCfgValueLine) { + PsiReference[] refs = element.getReferences(); + for (PsiReference ref : refs) { + if (ref instanceof BuildoutPartReference && ref.resolve() == null) { + unresolvedParts.add((BuildoutPartReference) ref); + } + } + + } + super.visitElement(element); //To change body of overridden methods use File | Settings | File Templates. + } + public List getUnresolvedParts() { + return unresolvedParts; + } + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/ImportFromToImportIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/ImportFromToImportIntention.java index 74449bd0..43ec65f3 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/ImportFromToImportIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/ImportFromToImportIntention.java @@ -16,11 +16,10 @@ package com.jetbrains.python.impl.codeInsight.intentions; import com.google.common.collect.Sets; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.psi.PyUtil; +import com.jetbrains.python.impl.psi.types.PyModuleType; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyPsiUtils; -import com.jetbrains.python.impl.psi.types.PyModuleType; import com.jetbrains.python.psi.types.TypeEvalContext; import consulo.codeEditor.Editor; import consulo.document.Document; @@ -33,7 +32,6 @@ import consulo.project.Project; import consulo.python.impl.localize.PyLocalize; import consulo.ui.NotificationType; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -325,7 +323,7 @@ public boolean execute(@Nonnull PsiElement element) { //myFromImportStatement.replace(new_import); } catch (IncorrectOperationException ignored) { - PyUtil.showBalloon(project, PyBundle.message("QFIX.action.failed"), NotificationType.WARNING); + PyUtil.showBalloon(project, PyLocalize.qfixActionFailed().get(), NotificationType.WARNING); } } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyBaseIntentionAction.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyBaseIntentionAction.java index a46a48a6..51164c57 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyBaseIntentionAction.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyBaseIntentionAction.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.codeInsight.intentions; - import jakarta.annotation.Nonnull; import consulo.language.editor.FileModificationService; @@ -26,15 +24,14 @@ import consulo.language.psi.PsiFile; import consulo.language.util.IncorrectOperationException; -public abstract class PyBaseIntentionAction extends BaseIntentionAction -{ - - @Override - public final void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException - { - if (!FileModificationService.getInstance().prepareFileForWrite(file)) return; - doInvoke(project, editor, file); - } +public abstract class PyBaseIntentionAction extends BaseIntentionAction { + @Override + public final void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + if (!FileModificationService.getInstance().prepareFileForWrite(file)) { + return; + } + doInvoke(project, editor, file); + } - public abstract void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException; + public abstract void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException; } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyDemorganIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyDemorganIntention.java index 461670d9..64a10e7b 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyDemorganIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyDemorganIntention.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.codeInsight.intentions; import com.jetbrains.python.PyTokenTypes; @@ -24,8 +23,8 @@ import consulo.language.psi.PsiFile; import consulo.language.psi.util.PsiTreeUtil; import consulo.language.util.IncorrectOperationException; +import consulo.localize.LocalizeValue; import consulo.project.Project; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -33,112 +32,113 @@ * @author yole */ public class PyDemorganIntention extends BaseIntentionAction { - @Nonnull - @Override - public String getText() { - return "DeMorgan Law"; - } - - @Override - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { - if (!(file instanceof PyFile)) { - return false; + @Nonnull + @Override + public LocalizeValue getText() { + return LocalizeValue.localizeTODO("DeMorgan Law"); } - PyBinaryExpression expression = - PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyBinaryExpression.class); - if (expression != null) { - PyElementType op = expression.getOperator(); - if (op == PyTokenTypes.AND_KEYWORD || op == PyTokenTypes.OR_KEYWORD) { - return true; - } + @Override + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (!(file instanceof PyFile)) { + return false; + } + + PyBinaryExpression expression = + PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyBinaryExpression.class); + if (expression != null) { + PyElementType op = expression.getOperator(); + if (op == PyTokenTypes.AND_KEYWORD || op == PyTokenTypes.OR_KEYWORD) { + return true; + } + } + return false; } - return false; - } - @Override - public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - PyBinaryExpression expression = PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), - PyBinaryExpression.class); - PyElementType op = expression.getOperator(); - String converted = convertConjunctionExpression(expression, op); - replaceExpression(converted, expression); - } - - private static void replaceExpression(String newExpression, PyBinaryExpression expression) { - PsiElement expressionToReplace = expression; - String expString = "not(" + newExpression + ')'; - PsiElement parent = expression.getParent().getParent(); - if (isNegation(parent)) { - expressionToReplace = parent; - expString = newExpression; + @Override + public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + PyBinaryExpression expression = PsiTreeUtil.getParentOfType( + file.findElementAt(editor.getCaretModel().getOffset()), + PyBinaryExpression.class + ); + PyElementType op = expression.getOperator(); + String converted = convertConjunctionExpression(expression, op); + replaceExpression(converted, expression); } - PyElementGenerator generator = PyElementGenerator.getInstance(expression.getProject()); - PyExpression newCall = generator.createExpressionFromText(expString); - PsiElement insertedElement = expressionToReplace.replace(newCall); - // codeStyleManager = expression.getManager().getCodeStyleManager() - // TODO codeStyleManager.reformat(insertedElement) - } - private static String convertConjunctionExpression(PyBinaryExpression exp, PyElementType tokenType) { - PyExpression lhs = exp.getLeftExpression(); - String lhsText; - String rhsText; - if (isConjunctionExpression(lhs, tokenType)) { - lhsText = convertConjunctionExpression((PyBinaryExpression)lhs, tokenType); - } - else { - lhsText = convertLeafExpression(lhs); + private static void replaceExpression(String newExpression, PyBinaryExpression expression) { + PsiElement expressionToReplace = expression; + String expString = "not(" + newExpression + ')'; + PsiElement parent = expression.getParent().getParent(); + if (isNegation(parent)) { + expressionToReplace = parent; + expString = newExpression; + } + PyElementGenerator generator = PyElementGenerator.getInstance(expression.getProject()); + PyExpression newCall = generator.createExpressionFromText(expString); + PsiElement insertedElement = expressionToReplace.replace(newCall); + // codeStyleManager = expression.getManager().getCodeStyleManager() + // TODO codeStyleManager.reformat(insertedElement) } - PyExpression rhs = exp.getRightExpression(); - if (isConjunctionExpression(rhs, tokenType)) { - rhsText = convertConjunctionExpression((PyBinaryExpression)rhs, tokenType); - } - else { - rhsText = convertLeafExpression(rhs); + private static String convertConjunctionExpression(PyBinaryExpression exp, PyElementType tokenType) { + PyExpression lhs = exp.getLeftExpression(); + String lhsText; + String rhsText; + if (isConjunctionExpression(lhs, tokenType)) { + lhsText = convertConjunctionExpression((PyBinaryExpression) lhs, tokenType); + } + else { + lhsText = convertLeafExpression(lhs); + } + + PyExpression rhs = exp.getRightExpression(); + if (isConjunctionExpression(rhs, tokenType)) { + rhsText = convertConjunctionExpression((PyBinaryExpression) rhs, tokenType); + } + else { + rhsText = convertLeafExpression(rhs); + } + + String flippedConjunction = (tokenType == PyTokenTypes.AND_KEYWORD) ? " or " : " and "; + return lhsText + flippedConjunction + rhsText; } - String flippedConjunction = (tokenType == PyTokenTypes.AND_KEYWORD) ? " or " : " and "; - return lhsText + flippedConjunction + rhsText; - } - - private static String convertLeafExpression(PyExpression condition) { - if (isNegation(condition)) { - PyExpression negated = getNegated(condition); - if (negated == null) { - return ""; - } - return negated.getText(); - } - else { - if (condition instanceof PyBinaryExpression) - return "not(" + condition.getText() + ")"; - return "not " + condition.getText(); + private static String convertLeafExpression(PyExpression condition) { + if (isNegation(condition)) { + PyExpression negated = getNegated(condition); + if (negated == null) { + return ""; + } + return negated.getText(); + } + else { + if (condition instanceof PyBinaryExpression) { + return "not(" + condition.getText() + ")"; + } + return "not " + condition.getText(); + } } - } - - @Nullable - private static PyExpression getNegated(PyExpression expression) { - PyExpression operand = ((PyPrefixExpression)expression).getOperand(); - return operand; // TODO strip () - } - private static boolean isConjunctionExpression(PyExpression expression, PyElementType tokenType) { - if (expression instanceof PyBinaryExpression) { - PyElementType operator = ((PyBinaryExpression)expression).getOperator(); - return operator == tokenType; + @Nullable + private static PyExpression getNegated(PyExpression expression) { + PyExpression operand = ((PyPrefixExpression) expression).getOperand(); + return operand; // TODO strip () } - return false; - } - private static boolean isNegation(PsiElement expression) { - if (!(expression instanceof PyPrefixExpression)) { - return false; + private static boolean isConjunctionExpression(PyExpression expression, PyElementType tokenType) { + if (expression instanceof PyBinaryExpression) { + PyElementType operator = ((PyBinaryExpression) expression).getOperator(); + return operator == tokenType; + } + return false; } - PyElementType op = ((PyPrefixExpression)expression).getOperator(); - return op == PyTokenTypes.NOT_KEYWORD; - } - + private static boolean isNegation(PsiElement expression) { + if (!(expression instanceof PyPrefixExpression)) { + return false; + } + PyElementType op = ((PyPrefixExpression) expression).getOperator(); + return op == PyTokenTypes.NOT_KEYWORD; + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyDictLiteralFormToConstructorIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyDictLiteralFormToConstructorIntention.java index b77c778f..78da8930 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyDictLiteralFormToConstructorIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyDictLiteralFormToConstructorIntention.java @@ -13,99 +13,103 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.codeInsight.intentions; -import jakarta.annotation.Nonnull; - -import consulo.language.editor.intention.BaseIntentionAction; +import com.jetbrains.python.PyNames; +import com.jetbrains.python.psi.*; import consulo.codeEditor.Editor; -import consulo.project.Project; -import consulo.language.util.IncorrectOperationException; -import consulo.util.lang.StringUtil; +import consulo.language.editor.intention.BaseIntentionAction; import consulo.language.psi.PsiFile; import consulo.language.psi.util.PsiTreeUtil; -import com.jetbrains.python.impl.PyBundle; -import com.jetbrains.python.PyNames; -import com.jetbrains.python.psi.*; +import consulo.language.util.IncorrectOperationException; +import consulo.localize.LocalizeValue; +import consulo.project.Project; +import consulo.python.impl.localize.PyLocalize; +import consulo.util.lang.StringUtil; +import jakarta.annotation.Nonnull; /** - * User: catherine - * * Intention to convert dict literal expression to dict constructor if the keys are all string constants on a literal dict. * For instance, * {} -> dict * {'a': 3, 'b': 5} -> dict(a=3, b=5) * {a: 3, b: 5} -> no transformation + * + * @author catherine */ public class PyDictLiteralFormToConstructorIntention extends BaseIntentionAction { - @Nonnull - public String getFamilyName() { - return PyBundle.message("INTN.convert.dict.literal.to.dict.constructor"); - } - - @Nonnull - public String getText() { - return PyBundle.message("INTN.convert.dict.literal.to.dict.constructor"); - } - - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { - if (!(file instanceof PyFile)) { - return false; + @Nonnull + @Override + public LocalizeValue getText() { + return PyLocalize.intnConvertDictLiteralToDictConstructor(); } - PyDictLiteralExpression dictExpression = - PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyDictLiteralExpression.class); + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (!(file instanceof PyFile)) { + return false; + } - if (dictExpression != null) { - PyKeyValueExpression[] elements = dictExpression.getElements(); - if (elements.length != 0) { - for (PyKeyValueExpression element : elements) { - PyExpression key = element.getKey(); - if (! (key instanceof PyStringLiteralExpression)) return false; - String str = ((PyStringLiteralExpression)key).getStringValue(); - if (PyNames.isReserved(str)) return false; + PyDictLiteralExpression dictExpression = + PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyDictLiteralExpression.class); - if(str.length() == 0 || Character.isDigit(str.charAt(0))) return false; - if (!StringUtil.isJavaIdentifier(str)) return false; + if (dictExpression != null) { + PyKeyValueExpression[] elements = dictExpression.getElements(); + if (elements.length != 0) { + for (PyKeyValueExpression element : elements) { + PyExpression key = element.getKey(); + if (!(key instanceof PyStringLiteralExpression)) { + return false; + } + String str = ((PyStringLiteralExpression) key).getStringValue(); + if (PyNames.isReserved(str)) { + return false; + } + + if (str.length() == 0 || Character.isDigit(str.charAt(0))) { + return false; + } + if (!StringUtil.isJavaIdentifier(str)) { + return false; + } + } + } + return true; } - } - return true; + return false; } - return false; - } - public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException - { - PyDictLiteralExpression dictExpression = - PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyDictLiteralExpression.class); - PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - if (dictExpression != null) { - replaceDictLiteral(dictExpression, elementGenerator); + public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + PyDictLiteralExpression dictExpression = + PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyDictLiteralExpression.class); + PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + if (dictExpression != null) { + replaceDictLiteral(dictExpression, elementGenerator); + } } - } - private static void replaceDictLiteral(PyDictLiteralExpression dictExpression, PyElementGenerator elementGenerator) { - PyExpression[] argumentList = dictExpression.getElements(); - StringBuilder stringBuilder = new StringBuilder("dict("); - int size = argumentList.length; - for (int i = 0; i != size; ++i) { - PyExpression argument = argumentList[i]; - if (argument instanceof PyKeyValueExpression) { - PyExpression key = ((PyKeyValueExpression)argument).getKey(); - PyExpression value = ((PyKeyValueExpression)argument).getValue(); - if (key instanceof PyStringLiteralExpression && value != null) { - stringBuilder.append(((PyStringLiteralExpression)key).getStringValue()); - stringBuilder.append("="); - stringBuilder.append(value.getText()); - if (i != size-1) - stringBuilder.append(", "); + private static void replaceDictLiteral(PyDictLiteralExpression dictExpression, PyElementGenerator elementGenerator) { + PyExpression[] argumentList = dictExpression.getElements(); + StringBuilder stringBuilder = new StringBuilder("dict("); + int size = argumentList.length; + for (int i = 0; i != size; ++i) { + PyExpression argument = argumentList[i]; + if (argument instanceof PyKeyValueExpression) { + PyExpression key = ((PyKeyValueExpression) argument).getKey(); + PyExpression value = ((PyKeyValueExpression) argument).getValue(); + if (key instanceof PyStringLiteralExpression && value != null) { + stringBuilder.append(((PyStringLiteralExpression) key).getStringValue()); + stringBuilder.append("="); + stringBuilder.append(value.getText()); + if (i != size - 1) { + stringBuilder.append(", "); + } + } + } } - } + stringBuilder.append(")"); + PyCallExpression callExpression = (PyCallExpression) elementGenerator.createFromText(LanguageLevel.forElement(dictExpression), + PyExpressionStatement.class, stringBuilder.toString() + ).getExpression(); + dictExpression.replace(callExpression); } - stringBuilder.append(")"); - PyCallExpression callExpression = (PyCallExpression)elementGenerator.createFromText(LanguageLevel.forElement(dictExpression), - PyExpressionStatement.class, stringBuilder.toString()).getExpression(); - dictExpression.replace(callExpression); - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyFlipComparisonIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyFlipComparisonIntention.java index b3afbba2..4a0809e4 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyFlipComparisonIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyFlipComparisonIntention.java @@ -13,92 +13,87 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.codeInsight.intentions; -import consulo.language.editor.intention.BaseIntentionAction; -import consulo.codeEditor.Editor; -import consulo.project.Project; -import consulo.language.psi.PsiElement; -import consulo.language.psi.PsiFile; -import consulo.language.psi.util.PsiTreeUtil; - -import java.util.HashMap; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.psi.PyBinaryExpression; import com.jetbrains.python.psi.PyElementGenerator; import com.jetbrains.python.psi.PyElementType; import com.jetbrains.python.psi.PyFile; +import consulo.codeEditor.Editor; +import consulo.language.editor.intention.BaseIntentionAction; +import consulo.language.psi.PsiElement; +import consulo.language.psi.PsiFile; +import consulo.language.psi.util.PsiTreeUtil; import consulo.language.util.IncorrectOperationException; - +import consulo.project.Project; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; +import java.util.HashMap; import java.util.Map; /** - * Created by IntelliJ IDEA. - * Author: Alexey.Ivanov - * Date: 26.03.2010 - * Time: 22:01:27 + * @author Alexey.Ivanov + * @since 2010-03-26 */ -public class PyFlipComparisonIntention extends BaseIntentionAction -{ - private static final Map FLIPPED_OPERATORS = new HashMap(7); - - static { - FLIPPED_OPERATORS.put(PyTokenTypes.EQEQ, "=="); - FLIPPED_OPERATORS.put(PyTokenTypes.NE, "!="); - FLIPPED_OPERATORS.put(PyTokenTypes.NE_OLD, "<>"); - FLIPPED_OPERATORS.put(PyTokenTypes.GE, "<="); - FLIPPED_OPERATORS.put(PyTokenTypes.LE, ">="); - FLIPPED_OPERATORS.put(PyTokenTypes.GT, "<"); - FLIPPED_OPERATORS.put(PyTokenTypes.LT, ">"); - } +public class PyFlipComparisonIntention extends BaseIntentionAction { + private static final Map FLIPPED_OPERATORS = new HashMap(7); - @Nonnull - public String getFamilyName() { - return PyBundle.message("INTN.flip.comparison"); - } + static { + FLIPPED_OPERATORS.put(PyTokenTypes.EQEQ, "=="); + FLIPPED_OPERATORS.put(PyTokenTypes.NE, "!="); + FLIPPED_OPERATORS.put(PyTokenTypes.NE_OLD, "<>"); + FLIPPED_OPERATORS.put(PyTokenTypes.GE, "<="); + FLIPPED_OPERATORS.put(PyTokenTypes.LE, ">="); + FLIPPED_OPERATORS.put(PyTokenTypes.GT, "<"); + FLIPPED_OPERATORS.put(PyTokenTypes.LT, ">"); + } - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { - if (!(file instanceof PyFile)) { - return false; + @Nonnull + public String getFamilyName() { + return PyLocalize.intnFlipComparison().get(); } - PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); - PyBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PyBinaryExpression.class, false); - while (binaryExpression != null) { - PyElementType operator = binaryExpression.getOperator(); - if (FLIPPED_OPERATORS.containsKey(operator)) { - String operatorText = binaryExpression.getPsiOperator().getText(); - String flippedOperatorText = FLIPPED_OPERATORS.get(operator); - if (flippedOperatorText.equals(operatorText)) { - setText(PyBundle.message("INTN.flip.$0", operatorText)); + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (!(file instanceof PyFile)) { + return false; } - else { - setText(PyBundle.message("INTN.flip.$0.to.$1", operatorText, flippedOperatorText)); + + PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); + PyBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PyBinaryExpression.class, false); + while (binaryExpression != null) { + PyElementType operator = binaryExpression.getOperator(); + if (FLIPPED_OPERATORS.containsKey(operator)) { + String operatorText = binaryExpression.getPsiOperator().getText(); + String flippedOperatorText = FLIPPED_OPERATORS.get(operator); + if (flippedOperatorText.equals(operatorText)) { + setText(PyLocalize.intnFlip$0(operatorText)); + } + else { + setText(PyLocalize.intnFlip$0To$1(operatorText, flippedOperatorText)); + } + return true; + } + binaryExpression = PsiTreeUtil.getParentOfType(binaryExpression, PyBinaryExpression.class); } - return true; - } - binaryExpression = PsiTreeUtil.getParentOfType(binaryExpression, PyBinaryExpression.class); + return false; } - return false; - } - public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException - { - PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); - PyBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PyBinaryExpression.class, false); - while (binaryExpression != null) { - if (FLIPPED_OPERATORS.containsKey(binaryExpression.getOperator())) { - PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - binaryExpression.replace(elementGenerator.createBinaryExpression(FLIPPED_OPERATORS.get(binaryExpression.getOperator()), - binaryExpression.getRightExpression(), - binaryExpression.getLeftExpression())); - return; - } - binaryExpression = PsiTreeUtil.getParentOfType(binaryExpression, PyBinaryExpression.class); + public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); + PyBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PyBinaryExpression.class, false); + while (binaryExpression != null) { + if (FLIPPED_OPERATORS.containsKey(binaryExpression.getOperator())) { + PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + binaryExpression.replace(elementGenerator.createBinaryExpression( + FLIPPED_OPERATORS.get(binaryExpression.getOperator()), + binaryExpression.getRightExpression(), + binaryExpression.getLeftExpression() + )); + return; + } + binaryExpression = PsiTreeUtil.getParentOfType(binaryExpression, PyBinaryExpression.class); + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyGenerateDocstringIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyGenerateDocstringIntention.java index 040a17bb..cfa86141 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyGenerateDocstringIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyGenerateDocstringIntention.java @@ -15,119 +15,94 @@ */ package com.jetbrains.python.impl.codeInsight.intentions; -import jakarta.annotation.Nonnull; -import jakarta.annotation.Nullable; - +import com.jetbrains.python.impl.documentation.docstrings.DocStringUtil; +import com.jetbrains.python.impl.documentation.docstrings.PyDocstringGenerator; +import com.jetbrains.python.impl.documentation.doctest.PyDocstringFile; +import com.jetbrains.python.impl.psi.PyUtil; +import com.jetbrains.python.psi.*; import consulo.codeEditor.Editor; -import consulo.project.Project; import consulo.language.psi.PsiElement; import consulo.language.psi.PsiFile; import consulo.language.psi.util.PsiTreeUtil; import consulo.language.util.IncorrectOperationException; -import com.jetbrains.python.impl.PyBundle; -import com.jetbrains.python.impl.documentation.docstrings.DocStringUtil; -import com.jetbrains.python.impl.documentation.docstrings.PyDocstringGenerator; -import com.jetbrains.python.impl.documentation.doctest.PyDocstringFile; -import com.jetbrains.python.psi.PyDocStringOwner; -import com.jetbrains.python.psi.PyFile; -import com.jetbrains.python.psi.PyFunction; -import com.jetbrains.python.psi.PyStatementList; -import com.jetbrains.python.psi.PyStringLiteralExpression; -import com.jetbrains.python.impl.psi.PyUtil; +import consulo.localize.LocalizeValue; +import consulo.project.Project; +import consulo.python.impl.localize.PyLocalize; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; /** - * User: catherine - * Intention to add documentation string for function - * (with checked format) + * Intention to add documentation string for function (with checked format). + * + * @author catherine */ -public class PyGenerateDocstringIntention extends PyBaseIntentionAction -{ - private String myText; - - @Nonnull - public String getFamilyName() - { - return PyBundle.message("INTN.doc.string.stub"); - } +public class PyGenerateDocstringIntention extends PyBaseIntentionAction { + @Nonnull + private LocalizeValue myText = LocalizeValue.empty(); - @Nonnull - @Override - public String getText() - { - return myText; - } + @Nonnull + @Override + public LocalizeValue getText() { + return myText; + } - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) - { - if(!(file instanceof PyFile) || file instanceof PyDocstringFile) - { - return false; - } - PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset()); - if(elementAt == null) - { - return false; - } - PyFunction function = PsiTreeUtil.getParentOfType(elementAt, PyFunction.class); - final PyStatementList statementList = PsiTreeUtil.getParentOfType(elementAt, PyStatementList.class, false, PyFunction.class); - if(function == null || statementList != null) - { - return false; - } - if(!elementAt.equals(function.getNameNode())) - { - return false; - } - return isAvailableForFunction(function); - } + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (!(file instanceof PyFile) || file instanceof PyDocstringFile) { + return false; + } + PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset()); + if (elementAt == null) { + return false; + } + PyFunction function = PsiTreeUtil.getParentOfType(elementAt, PyFunction.class); + final PyStatementList statementList = PsiTreeUtil.getParentOfType(elementAt, PyStatementList.class, false, PyFunction.class); + if (function == null || statementList != null) { + return false; + } + if (!elementAt.equals(function.getNameNode())) { + return false; + } + return isAvailableForFunction(function); + } - private boolean isAvailableForFunction(PyFunction function) - { - if(function.getDocStringValue() != null) - { - if(PyDocstringGenerator.forDocStringOwner(function).withInferredParameters(false).hasParametersToAdd()) - { - myText = PyBundle.message("INTN.add.parameters.to.docstring"); - return true; - } - else - { - return false; - } - } - else - { - myText = PyBundle.message("INTN.doc.string.stub"); - return true; - } - } + private boolean isAvailableForFunction(PyFunction function) { + if (function.getDocStringValue() != null) { + if (PyDocstringGenerator.forDocStringOwner(function).withInferredParameters(false).hasParametersToAdd()) { + myText = PyLocalize.intnAddParametersToDocstring(); + return true; + } + else { + return false; + } + } + else { + myText = PyLocalize.intnDocStringStub(); + return true; + } + } - public void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException - { - PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset()); - PyFunction function = PsiTreeUtil.getParentOfType(elementAt, PyFunction.class); + public void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset()); + PyFunction function = PsiTreeUtil.getParentOfType(elementAt, PyFunction.class); - if(function == null) - { - return; - } + if (function == null) { + return; + } - generateDocstring(function, editor); - } + generateDocstring(function, editor); + } - public static void generateDocstring(@Nonnull PyDocStringOwner docStringOwner, @Nullable Editor editor) - { - if(!DocStringUtil.ensureNotPlainDocstringFormat(docStringOwner)) - { - return; - } - final PyDocstringGenerator docstringGenerator = PyDocstringGenerator.forDocStringOwner(docStringOwner).withInferredParameters(false).addFirstEmptyLine(); - final PyStringLiteralExpression updated = docstringGenerator.buildAndInsert().getDocStringExpression(); - if(updated != null && editor != null) - { - final int offset = updated.getTextOffset(); - editor.getCaretModel().moveToOffset(offset); - editor.getCaretModel().moveCaretRelatively(0, 1, false, false, false); - } - } + public static void generateDocstring(@Nonnull PyDocStringOwner docStringOwner, @Nullable Editor editor) { + if (!DocStringUtil.ensureNotPlainDocstringFormat(docStringOwner)) { + return; + } + final PyDocstringGenerator docstringGenerator = + PyDocstringGenerator.forDocStringOwner(docStringOwner).withInferredParameters(false).addFirstEmptyLine(); + final PyStringLiteralExpression updated = docstringGenerator.buildAndInsert().getDocStringExpression(); + if (updated != null && editor != null) { + final int offset = updated.getTextOffset(); + editor.getCaretModel().moveToOffset(offset); + editor.getCaretModel().moveCaretRelatively(0, 1, false, false, false); + } + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyJoinIfIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyJoinIfIntention.java index 7550fbfe..2f173305 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyJoinIfIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyJoinIfIntention.java @@ -13,140 +13,153 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.codeInsight.intentions; -import consulo.language.editor.intention.BaseIntentionAction; +import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.psi.*; import consulo.codeEditor.Editor; -import consulo.project.Project; +import consulo.language.editor.intention.BaseIntentionAction; import consulo.language.psi.PsiComment; import consulo.language.psi.PsiElement; import consulo.language.psi.PsiFile; import consulo.language.psi.util.PsiTreeUtil; import consulo.language.util.IncorrectOperationException; -import com.jetbrains.python.impl.PyBundle; -import com.jetbrains.python.PyTokenTypes; -import com.jetbrains.python.psi.*; - +import consulo.localize.LocalizeValue; +import consulo.project.Project; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; import java.util.List; /** - * User: catherine - * Intention to merge the if clauses in the case of nested ifs where only the inner if contains code (the outer if only contains the inner one) - * For instance, - * if a: + * Intention to merge the if clauses in the case of nested ifs where only the inner if contains code + * (the outer if only contains the inner one). + * + *

For instance, + * if a: * if b: - * # stuff here + * # stuff here * into - * if a and b: - * #stuff here + * if a and b: + * #stuff here

+ * + * @author catherine */ -public class PyJoinIfIntention extends BaseIntentionAction -{ - @Nonnull - public String getFamilyName() { - return PyBundle.message("INTN.join.if"); - } - - @Nonnull - public String getText() { - return PyBundle.message("INTN.join.if.text"); - } - - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { - if (!(file instanceof PyFile)) { - return false; +public class PyJoinIfIntention extends BaseIntentionAction { + @Nonnull + @Override + public LocalizeValue getText() { + return PyLocalize.intnJoinIfText(); } - PyIfStatement expression = - PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyIfStatement.class); + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (!(file instanceof PyFile)) { + return false; + } - PyIfStatement outer = getIfStatement(expression); - if (outer != null) { - if (outer.getElsePart() != null || outer.getElifParts().length > 0) return false; - PyStatement firstStatement = getFirstStatement(outer); - PyStatementList outerStList = outer.getIfPart().getStatementList(); - if (outerStList != null && outerStList.getStatements().length != 1) return false; - if (firstStatement instanceof PyIfStatement) { - final PyIfStatement inner = (PyIfStatement)firstStatement; - if (inner.getElsePart() != null || inner.getElifParts().length > 0) return false; - PyStatementList stList = inner.getIfPart().getStatementList(); - if (stList != null) - if (stList.getStatements().length != 0) - return true; - } + PyIfStatement expression = + PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyIfStatement.class); + + PyIfStatement outer = getIfStatement(expression); + if (outer != null) { + if (outer.getElsePart() != null || outer.getElifParts().length > 0) { + return false; + } + PyStatement firstStatement = getFirstStatement(outer); + PyStatementList outerStList = outer.getIfPart().getStatementList(); + if (outerStList != null && outerStList.getStatements().length != 1) { + return false; + } + if (firstStatement instanceof PyIfStatement) { + final PyIfStatement inner = (PyIfStatement) firstStatement; + if (inner.getElsePart() != null || inner.getElifParts().length > 0) { + return false; + } + PyStatementList stList = inner.getIfPart().getStatementList(); + if (stList != null) { + if (stList.getStatements().length != 0) { + return true; + } + } + } + } + return false; } - return false; - } - public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - PyIfStatement expression = - PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyIfStatement.class); - PyIfStatement ifStatement = getIfStatement(expression); + public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + PyIfStatement expression = + PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyIfStatement.class); + PyIfStatement ifStatement = getIfStatement(expression); - PyStatement firstStatement = getFirstStatement(ifStatement); - if (ifStatement == null) return; - if (firstStatement != null && firstStatement instanceof PyIfStatement) { - PyExpression condition = ((PyIfStatement)firstStatement).getIfPart().getCondition(); - PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - PyExpression ifCondition = ifStatement.getIfPart().getCondition(); - if (ifCondition == null || condition == null) return; - StringBuilder replacementText = new StringBuilder(ifCondition.getText() + " and "); - if (condition instanceof PyBinaryExpression && ((PyBinaryExpression)condition).getOperator() == PyTokenTypes.OR_KEYWORD) { - replacementText.append("(").append(condition.getText()).append(")"); - } else - replacementText.append(condition.getText()); - - PyExpression newCondition = elementGenerator.createExpressionFromText(replacementText.toString()); - ifCondition.replace(newCondition); - - PyStatementList stList = ((PyIfStatement)firstStatement).getIfPart().getStatementList(); - PyStatementList ifStatementList = ifStatement.getIfPart().getStatementList(); - if (ifStatementList == null || stList == null) return; - List comments = PsiTreeUtil.getChildrenOfTypeAsList(ifStatement.getIfPart(), PsiComment.class); - comments.addAll(PsiTreeUtil.getChildrenOfTypeAsList(((PyIfStatement)firstStatement).getIfPart(), PsiComment.class)); - comments.addAll(PsiTreeUtil.getChildrenOfTypeAsList(ifStatementList, PsiComment.class)); - comments.addAll(PsiTreeUtil.getChildrenOfTypeAsList(stList, PsiComment.class)); - - for (PsiElement comm : comments) { - ifStatement.getIfPart().addBefore(comm, ifStatementList); - comm.delete(); - } - ifStatementList.replace(stList); + PyStatement firstStatement = getFirstStatement(ifStatement); + if (ifStatement == null) { + return; + } + if (firstStatement != null && firstStatement instanceof PyIfStatement) { + PyExpression condition = ((PyIfStatement) firstStatement).getIfPart().getCondition(); + PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + PyExpression ifCondition = ifStatement.getIfPart().getCondition(); + if (ifCondition == null || condition == null) { + return; + } + StringBuilder replacementText = new StringBuilder(ifCondition.getText() + " and "); + if (condition instanceof PyBinaryExpression && ((PyBinaryExpression) condition).getOperator() == PyTokenTypes.OR_KEYWORD) { + replacementText.append("(").append(condition.getText()).append(")"); + } + else { + replacementText.append(condition.getText()); + } + + PyExpression newCondition = elementGenerator.createExpressionFromText(replacementText.toString()); + ifCondition.replace(newCondition); + + PyStatementList stList = ((PyIfStatement) firstStatement).getIfPart().getStatementList(); + PyStatementList ifStatementList = ifStatement.getIfPart().getStatementList(); + if (ifStatementList == null || stList == null) { + return; + } + List comments = PsiTreeUtil.getChildrenOfTypeAsList(ifStatement.getIfPart(), PsiComment.class); + comments.addAll(PsiTreeUtil.getChildrenOfTypeAsList(((PyIfStatement) firstStatement).getIfPart(), PsiComment.class)); + comments.addAll(PsiTreeUtil.getChildrenOfTypeAsList(ifStatementList, PsiComment.class)); + comments.addAll(PsiTreeUtil.getChildrenOfTypeAsList(stList, PsiComment.class)); + + for (PsiElement comm : comments) { + ifStatement.getIfPart().addBefore(comm, ifStatementList); + comm.delete(); + } + ifStatementList.replace(stList); + } } - } - @Nullable - private static PyStatement getFirstStatement(PyIfStatement ifStatement) { - PyStatement firstStatement = null; - if (ifStatement != null) { - PyStatementList stList = ifStatement.getIfPart().getStatementList(); - if (stList != null) { - if (stList.getStatements().length != 0) { - firstStatement = stList.getStatements()[0]; + @Nullable + private static PyStatement getFirstStatement(PyIfStatement ifStatement) { + PyStatement firstStatement = null; + if (ifStatement != null) { + PyStatementList stList = ifStatement.getIfPart().getStatementList(); + if (stList != null) { + if (stList.getStatements().length != 0) { + firstStatement = stList.getStatements()[0]; + } + } } - } + return firstStatement; } - return firstStatement; - } - @Nullable - private static PyIfStatement getIfStatement(PyIfStatement expression) { - while (expression != null) { - PyStatementList stList = expression.getIfPart().getStatementList(); - if (stList != null) { - if (stList.getStatements().length != 0) { - PyStatement firstStatement = stList.getStatements()[0]; - if (firstStatement instanceof PyIfStatement) { - break; - } + @Nullable + private static PyIfStatement getIfStatement(PyIfStatement expression) { + while (expression != null) { + PyStatementList stList = expression.getIfPart().getStatementList(); + if (stList != null) { + if (stList.getStatements().length != 0) { + PyStatement firstStatement = stList.getStatements()[0]; + if (firstStatement instanceof PyIfStatement) { + break; + } + } + } + expression = PsiTreeUtil.getParentOfType(expression, PyIfStatement.class); } - } - expression = PsiTreeUtil.getParentOfType(expression, PyIfStatement.class); + return expression; } - return expression; - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyNegateComparisonIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyNegateComparisonIntention.java index b2145e9c..5310b2ef 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyNegateComparisonIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyNegateComparisonIntention.java @@ -13,105 +13,98 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.codeInsight.intentions; -import consulo.language.editor.intention.BaseIntentionAction; +import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.psi.*; import consulo.codeEditor.Editor; -import consulo.project.Project; +import consulo.language.editor.intention.BaseIntentionAction; import consulo.language.psi.PsiElement; import consulo.language.psi.PsiFile; import consulo.language.psi.util.PsiTreeUtil; import consulo.language.util.IncorrectOperationException; -import com.jetbrains.python.impl.PyBundle; -import com.jetbrains.python.PyTokenTypes; -import com.jetbrains.python.psi.*; - +import consulo.project.Project; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; import java.util.HashMap; import java.util.Map; /** - * Created by IntelliJ IDEA. - * Author: Alexey.Ivanov - * Date: 12.03.2010 - * Time: 17:58:56 + * @author Alexey.Ivanov + * @since 2010-03-12 */ -public class PyNegateComparisonIntention extends BaseIntentionAction -{ - private static final Map comparisonStrings = new HashMap(7); - private static final Map invertedComparasions = new HashMap(7); - - static { - comparisonStrings.put(PyTokenTypes.LT, "<"); - comparisonStrings.put(PyTokenTypes.GT, ">"); - comparisonStrings.put(PyTokenTypes.EQEQ, "=="); - comparisonStrings.put(PyTokenTypes.LE, "<="); - comparisonStrings.put(PyTokenTypes.GE, ">="); - comparisonStrings.put(PyTokenTypes.NE, "!="); - comparisonStrings.put(PyTokenTypes.NE_OLD, "<>"); +public class PyNegateComparisonIntention extends BaseIntentionAction { + private static final Map comparisonStrings = new HashMap(7); + private static final Map invertedComparasions = new HashMap(7); - invertedComparasions.put(PyTokenTypes.LT, PyTokenTypes.GE); - invertedComparasions.put(PyTokenTypes.GT, PyTokenTypes.LE); - invertedComparasions.put(PyTokenTypes.EQEQ, PyTokenTypes.NE); - invertedComparasions.put(PyTokenTypes.LE, PyTokenTypes.GT); - invertedComparasions.put(PyTokenTypes.GE, PyTokenTypes.LT); - invertedComparasions.put(PyTokenTypes.NE, PyTokenTypes.EQEQ); - invertedComparasions.put(PyTokenTypes.NE_OLD, PyTokenTypes.EQEQ); - } + static { + comparisonStrings.put(PyTokenTypes.LT, "<"); + comparisonStrings.put(PyTokenTypes.GT, ">"); + comparisonStrings.put(PyTokenTypes.EQEQ, "=="); + comparisonStrings.put(PyTokenTypes.LE, "<="); + comparisonStrings.put(PyTokenTypes.GE, ">="); + comparisonStrings.put(PyTokenTypes.NE, "!="); + comparisonStrings.put(PyTokenTypes.NE_OLD, "<>"); - @Nonnull - public String getFamilyName() { - return PyBundle.message("INTN.negate.comparison"); - } - - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { - if (!(file instanceof PyFile)) { - return false; + invertedComparasions.put(PyTokenTypes.LT, PyTokenTypes.GE); + invertedComparasions.put(PyTokenTypes.GT, PyTokenTypes.LE); + invertedComparasions.put(PyTokenTypes.EQEQ, PyTokenTypes.NE); + invertedComparasions.put(PyTokenTypes.LE, PyTokenTypes.GT); + invertedComparasions.put(PyTokenTypes.GE, PyTokenTypes.LT); + invertedComparasions.put(PyTokenTypes.NE, PyTokenTypes.EQEQ); + invertedComparasions.put(PyTokenTypes.NE_OLD, PyTokenTypes.EQEQ); } - PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); - PyBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PyBinaryExpression.class, false); - while (binaryExpression != null) { - PyElementType operator = binaryExpression.getOperator(); - if (comparisonStrings.containsKey(operator)) { - setText(PyBundle.message("INTN.negate.$0.to.$1", comparisonStrings.get(operator), - comparisonStrings.get(invertedComparasions.get(operator)))); - return true; - } - binaryExpression = PsiTreeUtil.getParentOfType(binaryExpression, PyBinaryExpression.class); + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (!(file instanceof PyFile)) { + return false; + } + + PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); + PyBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PyBinaryExpression.class, false); + while (binaryExpression != null) { + PyElementType operator = binaryExpression.getOperator(); + if (comparisonStrings.containsKey(operator)) { + setText(PyLocalize.intnNegate$0To$1( + comparisonStrings.get(operator), + comparisonStrings.get(invertedComparasions.get(operator)) + )); + return true; + } + binaryExpression = PsiTreeUtil.getParentOfType(binaryExpression, PyBinaryExpression.class); + } + return false; } - return false; - } - public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); - PyBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PyBinaryExpression.class, false); - while (binaryExpression != null) { - PyElementType operator = binaryExpression.getOperator(); - if (comparisonStrings.containsKey(operator)) { - PsiElement parent = binaryExpression.getParent(); - while (parent instanceof PyParenthesizedExpression) { - parent = parent.getParent(); - } + PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); + PyBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PyBinaryExpression.class, false); + while (binaryExpression != null) { + PyElementType operator = binaryExpression.getOperator(); + if (comparisonStrings.containsKey(operator)) { + PsiElement parent = binaryExpression.getParent(); + while (parent instanceof PyParenthesizedExpression) { + parent = parent.getParent(); + } - final PyElementType invertedOperator = invertedComparasions.get(binaryExpression.getOperator()); - PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - final PyBinaryExpression newElement = elementGenerator - .createBinaryExpression(comparisonStrings.get(invertedOperator), binaryExpression.getLeftExpression(), - binaryExpression.getRightExpression()); + final PyElementType invertedOperator = invertedComparasions.get(binaryExpression.getOperator()); + PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + final PyBinaryExpression newElement = elementGenerator + .createBinaryExpression(comparisonStrings.get(invertedOperator), binaryExpression.getLeftExpression(), + binaryExpression.getRightExpression() + ); - if (parent instanceof PyPrefixExpression && ((PyPrefixExpression)parent).getOperator() == PyTokenTypes.NOT_KEYWORD) { - parent.replace(newElement); - } - else { - binaryExpression.replace(elementGenerator.createExpressionFromText("not " + newElement.getText())); + if (parent instanceof PyPrefixExpression && ((PyPrefixExpression) parent).getOperator() == PyTokenTypes.NOT_KEYWORD) { + parent.replace(newElement); + } + else { + binaryExpression.replace(elementGenerator.createExpressionFromText("not " + newElement.getText())); + } + return; + } + binaryExpression = PsiTreeUtil.getParentOfType(binaryExpression, PyBinaryExpression.class); } - return; - } - binaryExpression = PsiTreeUtil.getParentOfType(binaryExpression, PyBinaryExpression.class); } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyQuotedStringIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyQuotedStringIntention.java index 6ce7aa6a..c221feaa 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyQuotedStringIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyQuotedStringIntention.java @@ -13,145 +13,143 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.codeInsight.intentions; -import jakarta.annotation.Nonnull; - -import consulo.language.editor.intention.BaseIntentionAction; -import consulo.codeEditor.Editor; -import consulo.project.Project; -import consulo.language.psi.PsiFile; -import consulo.language.psi.util.PsiTreeUtil; -import com.jetbrains.python.impl.PyBundle; +import com.jetbrains.python.impl.psi.impl.PyStringLiteralExpressionImpl; import com.jetbrains.python.psi.PyDocStringOwner; import com.jetbrains.python.psi.PyElementGenerator; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.PyStringLiteralExpression; -import com.jetbrains.python.impl.psi.impl.PyStringLiteralExpressionImpl; +import consulo.codeEditor.Editor; +import consulo.language.editor.intention.BaseIntentionAction; +import consulo.language.psi.PsiFile; +import consulo.language.psi.util.PsiTreeUtil; import consulo.language.util.IncorrectOperationException; +import consulo.project.Project; +import consulo.python.impl.localize.PyLocalize; +import jakarta.annotation.Nonnull; /** - * User: catherine - * Intention to convert between single-quoted and double-quoted strings + * Intention to convert between single-quoted and double-quoted strings. + * + * @author catherine */ public class PyQuotedStringIntention extends BaseIntentionAction { + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (!(file instanceof PyFile)) { + return false; + } - @Nonnull - public String getFamilyName() { - return PyBundle.message("INTN.quoted.string"); - } + PyStringLiteralExpression string = + PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyStringLiteralExpression.class); + if (string != null) { + final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(string, PyDocStringOwner.class); + if (docStringOwner != null) { + if (docStringOwner.getDocStringExpression() == string) { + return false; + } + } + String stringText = string.getText(); + int prefixLength = PyStringLiteralExpressionImpl.getPrefixLength(stringText); + stringText = stringText.substring(prefixLength); - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { - if (!(file instanceof PyFile)) { - return false; + if (stringText.length() >= 6) { + if (stringText.startsWith("'''") && stringText.endsWith("'''") || + stringText.startsWith("\"\"\"") && stringText.endsWith("\"\"\"")) { + return false; + } + } + if (stringText.length() > 2) { + if (stringText.startsWith("'") && stringText.endsWith("'")) { + setText(PyLocalize.intnQuotedStringSingleToDouble()); + return true; + } + if (stringText.startsWith("\"") && stringText.endsWith("\"")) { + setText(PyLocalize.intnQuotedStringDoubleToSingle()); + return true; + } + } + } + return false; } - PyStringLiteralExpression string = PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyStringLiteralExpression.class); - if (string != null) { - final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(string, PyDocStringOwner.class); - if (docStringOwner != null) { - if (docStringOwner.getDocStringExpression() == string) return false; - } - String stringText = string.getText(); - int prefixLength = PyStringLiteralExpressionImpl.getPrefixLength(stringText); - stringText = stringText.substring(prefixLength); + public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + PyStringLiteralExpression string = + PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyStringLiteralExpression.class); + PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + if (string != null) { + final String stringText = string.getText(); + int prefixLength = PyStringLiteralExpressionImpl.getPrefixLength(stringText); + final String text = stringText.substring(prefixLength); - if (stringText.length() >= 6) { - if (stringText.startsWith("'''") && stringText.endsWith("'''") || - stringText.startsWith("\"\"\"") && stringText.endsWith("\"\"\"")) return false; - } - if (stringText.length() > 2) { - if (stringText.startsWith("'") && stringText.endsWith("'")) { - setText(PyBundle.message("INTN.quoted.string.single.to.double")); - return true; + if (text.startsWith("'") && text.endsWith("'")) { + String result = convertSingleToDoubleQuoted(stringText); + PyStringLiteralExpression st = elementGenerator.createStringLiteralAlreadyEscaped(result); + string.replace(st); + } + if (text.startsWith("\"") && text.endsWith("\"")) { + String result = convertDoubleToSingleQuoted(stringText); + PyStringLiteralExpression st = elementGenerator.createStringLiteralAlreadyEscaped(result); + string.replace(st); + } } - if (stringText.startsWith("\"") && stringText.endsWith("\"")) { - setText(PyBundle.message("INTN.quoted.string.double.to.single")); - return true; - } - } } - return false; - } - - public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException - { - PyStringLiteralExpression string = PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyStringLiteralExpression.class); - PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - if (string != null) { - final String stringText = string.getText(); - int prefixLength = PyStringLiteralExpressionImpl.getPrefixLength(stringText); - final String text = stringText.substring(prefixLength); - if (text.startsWith("'") && text.endsWith("'")) { - String result = convertSingleToDoubleQuoted(stringText); - PyStringLiteralExpression st = elementGenerator.createStringLiteralAlreadyEscaped(result); - string.replace(st); - } - if (text.startsWith("\"") && text.endsWith("\"")) { - String result = convertDoubleToSingleQuoted(stringText); - PyStringLiteralExpression st = elementGenerator.createStringLiteralAlreadyEscaped(result); - string.replace(st); - } - } - } + private static String convertDoubleToSingleQuoted(String stringText) { + StringBuilder stringBuilder = new StringBuilder(); - private static String convertDoubleToSingleQuoted(String stringText) { - StringBuilder stringBuilder = new StringBuilder(); + boolean skipNext = false; + char[] charArr = stringText.toCharArray(); + for (int i = 0; i != charArr.length; ++i) { + char ch = charArr[i]; + if (skipNext) { + skipNext = false; + continue; + } + if (ch == '"') { + stringBuilder.append('\''); + continue; + } + else if (ch == '\'') { + stringBuilder.append("\\\'"); + } + else if (ch == '\\' && charArr[i + 1] == '\"' && !(i + 2 == charArr.length)) { + skipNext = true; + stringBuilder.append(charArr[i + 1]); + } + else { + stringBuilder.append(ch); + } + } - boolean skipNext = false; - char[] charArr = stringText.toCharArray(); - for (int i = 0; i != charArr.length; ++i) { - char ch = charArr[i]; - if (skipNext) { - skipNext = false; - continue; - } - if (ch == '"') { - stringBuilder.append('\''); - continue; - } - else if (ch == '\'') { - stringBuilder.append("\\\'"); - } - else if (ch == '\\' && charArr[i+1] == '\"' && !(i+2 == charArr.length)) { - skipNext = true; - stringBuilder.append(charArr[i+1]); - } - else { - stringBuilder.append(ch); - } + return stringBuilder.toString(); } - return stringBuilder.toString(); - } - - private static String convertSingleToDoubleQuoted(String stringText) { - StringBuilder stringBuilder = new StringBuilder(); - boolean skipNext = false; - char[] charArr = stringText.toCharArray(); - for (int i = 0; i != charArr.length; ++i) { - char ch = charArr[i]; - if (skipNext) { - skipNext = false; - continue; - } - if (ch == '\'') { - stringBuilder.append('"'); - continue; - } - else if (ch == '"') { - stringBuilder.append("\\\""); - } - else if (ch == '\\' && charArr[i+1] == '\'' && !(i+2 == charArr.length)) { - skipNext = true; - stringBuilder.append(charArr[i+1]); - } - else { - stringBuilder.append(ch); - } + private static String convertSingleToDoubleQuoted(String stringText) { + StringBuilder stringBuilder = new StringBuilder(); + boolean skipNext = false; + char[] charArr = stringText.toCharArray(); + for (int i = 0; i != charArr.length; ++i) { + char ch = charArr[i]; + if (skipNext) { + skipNext = false; + continue; + } + if (ch == '\'') { + stringBuilder.append('"'); + continue; + } + else if (ch == '"') { + stringBuilder.append("\\\""); + } + else if (ch == '\\' && charArr[i + 1] == '\'' && !(i + 2 == charArr.length)) { + skipNext = true; + stringBuilder.append(charArr[i + 1]); + } + else { + stringBuilder.append(ch); + } + } + return stringBuilder.toString(); } - return stringBuilder.toString(); - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PySplitIfIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PySplitIfIntention.java index df78c174..3b81d1b3 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PySplitIfIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PySplitIfIntention.java @@ -15,130 +15,101 @@ */ package com.jetbrains.python.impl.codeInsight.intentions; -import jakarta.annotation.Nonnull; - +import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.impl.PyPsiUtils; import consulo.codeEditor.Editor; -import consulo.project.Project; +import consulo.language.ast.IElementType; import consulo.language.psi.PsiElement; import consulo.language.psi.PsiFile; -import consulo.language.ast.IElementType; import consulo.language.psi.util.PsiTreeUtil; import consulo.language.util.IncorrectOperationException; -import com.jetbrains.python.impl.PyBundle; -import com.jetbrains.python.PyTokenTypes; -import com.jetbrains.python.psi.LanguageLevel; -import com.jetbrains.python.psi.PyBinaryExpression; -import com.jetbrains.python.psi.PyElementGenerator; -import com.jetbrains.python.psi.PyFile; -import com.jetbrains.python.psi.PyIfPart; -import com.jetbrains.python.psi.PyIfStatement; -import com.jetbrains.python.psi.PyStatementList; -import com.jetbrains.python.psi.impl.PyPsiUtils; +import consulo.project.Project; +import consulo.python.impl.localize.PyLocalize; +import jakarta.annotation.Nonnull; /** - * Created by IntelliJ IDEA. - * Author: Alexey.Ivanov - * Date: 10.03.2010 - * Time: 18:52:52 + * @author Alexey.Ivanov + * @since 2010-03-10 */ -public class PySplitIfIntention extends PyBaseIntentionAction -{ - @Nonnull - public String getFamilyName() - { - return PyBundle.message("INTN.split.if"); - } - - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) - { - if(!(file instanceof PyFile)) - { - return false; - } +public class PySplitIfIntention extends PyBaseIntentionAction { + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (!(file instanceof PyFile)) { + return false; + } - PsiElement elementAtOffset = file.findElementAt(editor.getCaretModel().getOffset()); - if(elementAtOffset == null || elementAtOffset.getNode() == null) - { - return false; - } + PsiElement elementAtOffset = file.findElementAt(editor.getCaretModel().getOffset()); + if (elementAtOffset == null || elementAtOffset.getNode() == null) { + return false; + } - // PY-745 - final IElementType elementType = elementAtOffset.getNode().getElementType(); - if(elementType == PyTokenTypes.COLON) - { - elementAtOffset = elementAtOffset.getPrevSibling(); - elementAtOffset = PyPsiUtils.getPrevNonCommentSibling(elementAtOffset, false); - } - else if(elementType == PyTokenTypes.IF_KEYWORD) - { - elementAtOffset = elementAtOffset.getNextSibling(); - elementAtOffset = PyPsiUtils.getNextNonCommentSibling(elementAtOffset, false); - } + // PY-745 + final IElementType elementType = elementAtOffset.getNode().getElementType(); + if (elementType == PyTokenTypes.COLON) { + elementAtOffset = elementAtOffset.getPrevSibling(); + elementAtOffset = PyPsiUtils.getPrevNonCommentSibling(elementAtOffset, false); + } + else if (elementType == PyTokenTypes.IF_KEYWORD) { + elementAtOffset = elementAtOffset.getNextSibling(); + elementAtOffset = PyPsiUtils.getNextNonCommentSibling(elementAtOffset, false); + } - PsiElement element = PsiTreeUtil.getParentOfType(elementAtOffset, PyBinaryExpression.class, false); - if(element == null) - { - return false; - } + PsiElement element = PsiTreeUtil.getParentOfType(elementAtOffset, PyBinaryExpression.class, false); + if (element == null) { + return false; + } - while(element.getParent() instanceof PyBinaryExpression) - { - element = element.getParent(); - } - if(((PyBinaryExpression) element).getOperator() != PyTokenTypes.AND_KEYWORD || ((PyBinaryExpression) element).getRightExpression() == null) - { - return false; - } - final PsiElement parent = element.getParent(); - if(!(parent instanceof PyIfPart)) - { - return false; - } - setText(PyBundle.message("INTN.split.if.text")); - return true; - } + while (element.getParent() instanceof PyBinaryExpression) { + element = element.getParent(); + } + if (((PyBinaryExpression) element).getOperator() != PyTokenTypes.AND_KEYWORD || ((PyBinaryExpression) element).getRightExpression() == null) { + return false; + } + final PsiElement parent = element.getParent(); + if (!(parent instanceof PyIfPart)) { + return false; + } + setText(PyLocalize.intnSplitIfText()); + return true; + } - public void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException - { - PsiElement elementAtOffset = file.findElementAt(editor.getCaretModel().getOffset()); - // PY-745 - final IElementType elementType = elementAtOffset.getNode().getElementType(); - if(elementType == PyTokenTypes.COLON) - { - elementAtOffset = elementAtOffset.getPrevSibling(); - elementAtOffset = PyPsiUtils.getPrevNonCommentSibling(elementAtOffset, false); - } - else if(elementType == PyTokenTypes.IF_KEYWORD) - { - elementAtOffset = elementAtOffset.getNextSibling(); - elementAtOffset = PyPsiUtils.getNextNonCommentSibling(elementAtOffset, false); - } + public void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + PsiElement elementAtOffset = file.findElementAt(editor.getCaretModel().getOffset()); + // PY-745 + final IElementType elementType = elementAtOffset.getNode().getElementType(); + if (elementType == PyTokenTypes.COLON) { + elementAtOffset = elementAtOffset.getPrevSibling(); + elementAtOffset = PyPsiUtils.getPrevNonCommentSibling(elementAtOffset, false); + } + else if (elementType == PyTokenTypes.IF_KEYWORD) { + elementAtOffset = elementAtOffset.getNextSibling(); + elementAtOffset = PyPsiUtils.getNextNonCommentSibling(elementAtOffset, false); + } - PyBinaryExpression element = PsiTreeUtil.getParentOfType(elementAtOffset, PyBinaryExpression.class, false); - while(element.getParent() instanceof PyBinaryExpression) - { - element = (PyBinaryExpression) element.getParent(); - } - PyIfStatement ifStatement = PsiTreeUtil.getParentOfType(element, PyIfStatement.class); - PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + PyBinaryExpression element = PsiTreeUtil.getParentOfType(elementAtOffset, PyBinaryExpression.class, false); + while (element.getParent() instanceof PyBinaryExpression) { + element = (PyBinaryExpression) element.getParent(); + } + PyIfStatement ifStatement = PsiTreeUtil.getParentOfType(element, PyIfStatement.class); + PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - PyIfStatement subIf = (PyIfStatement) ifStatement.copy(); + PyIfStatement subIf = (PyIfStatement) ifStatement.copy(); - subIf.getIfPart().getCondition().replace(element.getRightExpression()); - ifStatement.getIfPart().getCondition().replace(element.getLeftExpression()); - PyStatementList statementList = elementGenerator.createFromText(LanguageLevel.getDefault(), PyIfStatement.class, "if a:\n a = 1").getIfPart().getStatementList(); - statementList.getStatements()[0].replace(subIf); - PyIfStatement newIf = elementGenerator.createFromText(LanguageLevel.getDefault(), PyIfStatement.class, "if a:\n a = 1"); - newIf.getIfPart().getCondition().replace(ifStatement.getIfPart().getCondition()); - newIf.getIfPart().getStatementList().replace(statementList); - for(PyIfPart elif : ifStatement.getElifParts()) - { - newIf.add(elif); - } - if(ifStatement.getElsePart() != null) - { - newIf.add(ifStatement.getElsePart()); - } - ifStatement.replace(newIf); - } + subIf.getIfPart().getCondition().replace(element.getRightExpression()); + ifStatement.getIfPart().getCondition().replace(element.getLeftExpression()); + PyStatementList statementList = elementGenerator.createFromText(LanguageLevel.getDefault(), PyIfStatement.class, "if a:\n a = 1") + .getIfPart() + .getStatementList(); + statementList.getStatements()[0].replace(subIf); + PyIfStatement newIf = elementGenerator.createFromText(LanguageLevel.getDefault(), PyIfStatement.class, "if a:\n a = 1"); + newIf.getIfPart().getCondition().replace(ifStatement.getIfPart().getCondition()); + newIf.getIfPart().getStatementList().replace(statementList); + for (PyIfPart elif : ifStatement.getElifParts()) { + newIf.add(elif); + } + if (ifStatement.getElsePart() != null) { + newIf.add(ifStatement.getElsePart()); + } + ifStatement.replace(newIf); + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyStringConcatenationToFormatIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyStringConcatenationToFormatIntention.java index a5e420ef..329c76b3 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyStringConcatenationToFormatIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyStringConcatenationToFormatIntention.java @@ -15,14 +15,13 @@ */ package com.jetbrains.python.impl.codeInsight.intentions; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.impl.psi.PyStringLiteralUtil; -import com.jetbrains.python.psi.*; import com.jetbrains.python.impl.psi.impl.PyBuiltinCache; import com.jetbrains.python.impl.psi.types.PyClassTypeImpl; -import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.impl.psi.types.PyTypeChecker; +import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; import consulo.codeEditor.Editor; import consulo.ide.impl.idea.util.NotNullFunction; @@ -31,10 +30,11 @@ import consulo.language.psi.util.PsiTreeUtil; import consulo.language.util.IncorrectOperationException; import consulo.project.Project; +import consulo.python.impl.localize.PyLocalize; import consulo.util.lang.Pair; import consulo.util.lang.StringUtil; - import jakarta.annotation.Nonnull; + import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -43,171 +43,170 @@ * @author Alexey.Ivanov */ public class PyStringConcatenationToFormatIntention extends PyBaseIntentionAction { + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (!(file instanceof PyFile)) { + return false; + } - @Nonnull - public String getFamilyName() { - return PyBundle.message("INTN.string.concatenation.to.format"); - } - - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { - if (!(file instanceof PyFile)) { - return false; - } - - PsiElement element = - PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyBinaryExpression.class, false); + PsiElement element = + PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyBinaryExpression.class, false); - if (element == null) { - return false; - } - while (element.getParent() instanceof PyBinaryExpression) { - element = element.getParent(); - } + if (element == null) { + return false; + } + while (element.getParent() instanceof PyBinaryExpression) { + element = element.getParent(); + } - final Collection operators = getOperators((PyBinaryExpression)element); - for (PyElementType operator : operators) { - if (operator != PyTokenTypes.PLUS) { - return false; - } - } + final Collection operators = getOperators((PyBinaryExpression) element); + for (PyElementType operator : operators) { + if (operator != PyTokenTypes.PLUS) { + return false; + } + } - final Collection expressions = getSimpleExpressions((PyBinaryExpression)element); - if (expressions.size() == 0) { - return false; - } - final PyBuiltinCache cache = PyBuiltinCache.getInstance(element); - for (PyExpression expression : expressions) { - if (expression == null) { - return false; - } - if (expression instanceof PyStringLiteralExpression) { - continue; - } - final PyType type = TypeEvalContext.codeAnalysis(file.getProject(), file).getType(expression); - final boolean isStringReference = PyTypeChecker.match(cache.getStringType(LanguageLevel.forElement(expression)), - type, - TypeEvalContext.codeAnalysis(file.getProject(), file)) && type != - null; - if (!isStringReference) { - return false; - } - } - if (LanguageLevel.forElement(element).isAtLeast(LanguageLevel.PYTHON27)) { - setText(PyBundle.message("INTN.replace.plus.with.str.format")); - } - else { - setText(PyBundle.message("INTN.replace.plus.with.format.operator")); + final Collection expressions = getSimpleExpressions((PyBinaryExpression) element); + if (expressions.size() == 0) { + return false; + } + final PyBuiltinCache cache = PyBuiltinCache.getInstance(element); + for (PyExpression expression : expressions) { + if (expression == null) { + return false; + } + if (expression instanceof PyStringLiteralExpression) { + continue; + } + final PyType type = TypeEvalContext.codeAnalysis(file.getProject(), file).getType(expression); + final boolean isStringReference = PyTypeChecker.match( + cache.getStringType(LanguageLevel.forElement(expression)), + type, + TypeEvalContext.codeAnalysis(file.getProject(), file) + ) && type != + null; + if (!isStringReference) { + return false; + } + } + if (LanguageLevel.forElement(element).isAtLeast(LanguageLevel.PYTHON27)) { + setText(PyLocalize.intnReplacePlusWithStrFormat()); + } + else { + setText(PyLocalize.intnReplacePlusWithFormatOperator()); + } + return true; } - return true; - } - private static Collection getSimpleExpressions(@Nonnull PyBinaryExpression expression) { - List res = new ArrayList<>(); - if (expression.getLeftExpression() instanceof PyBinaryExpression) { - res.addAll(getSimpleExpressions((PyBinaryExpression)expression.getLeftExpression())); - } - else { - res.add(expression.getLeftExpression()); - } - if (expression.getRightExpression() instanceof PyBinaryExpression) { - res.addAll(getSimpleExpressions((PyBinaryExpression)expression.getRightExpression())); - } - else { - res.add(expression.getRightExpression()); + private static Collection getSimpleExpressions(@Nonnull PyBinaryExpression expression) { + List res = new ArrayList<>(); + if (expression.getLeftExpression() instanceof PyBinaryExpression) { + res.addAll(getSimpleExpressions((PyBinaryExpression) expression.getLeftExpression())); + } + else { + res.add(expression.getLeftExpression()); + } + if (expression.getRightExpression() instanceof PyBinaryExpression) { + res.addAll(getSimpleExpressions((PyBinaryExpression) expression.getRightExpression())); + } + else { + res.add(expression.getRightExpression()); + } + return res; } - return res; - } - private static Collection getOperators(@Nonnull PyBinaryExpression expression) { - List res = new ArrayList<>(); - if (expression.getLeftExpression() instanceof PyBinaryExpression) { - res.addAll(getOperators((PyBinaryExpression)expression.getLeftExpression())); - } - if (expression.getRightExpression() instanceof PyBinaryExpression) { - res.addAll(getOperators((PyBinaryExpression)expression.getRightExpression())); + private static Collection getOperators(@Nonnull PyBinaryExpression expression) { + List res = new ArrayList<>(); + if (expression.getLeftExpression() instanceof PyBinaryExpression) { + res.addAll(getOperators((PyBinaryExpression) expression.getLeftExpression())); + } + if (expression.getRightExpression() instanceof PyBinaryExpression) { + res.addAll(getOperators((PyBinaryExpression) expression.getRightExpression())); + } + res.add(expression.getOperator()); + return res; } - res.add(expression.getOperator()); - return res; - } - public void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - PsiElement element = - PsiTreeUtil.getTopmostParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyBinaryExpression.class); + public void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + PsiElement element = + PsiTreeUtil.getTopmostParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyBinaryExpression.class); - if (element == null) { - return; - } - final LanguageLevel languageLevel = LanguageLevel.forElement(element); - final boolean useFormatMethod = languageLevel.isAtLeast(LanguageLevel.PYTHON27); - - NotNullFunction escaper = consulo.ide.impl.idea.openapi.util.text.StringUtil.escaper(false, "\"\'\\"); - StringBuilder stringLiteral = new StringBuilder(); - List parameters = new ArrayList<>(); - Pair quotes = Pair.create("\"", "\""); - boolean quotesDetected = false; - final TypeEvalContext context = TypeEvalContext.userInitiated(file.getProject(), file); - int paramCount = 0; - boolean isUnicode = false; - final PyClassTypeImpl unicodeType = PyBuiltinCache.getInstance(element).getObjectType("unicode"); - - for (PyExpression expression : getSimpleExpressions((PyBinaryExpression)element)) { - if (expression instanceof PyStringLiteralExpression) { - final PyType type = context.getType(expression); - if (type != null && type.equals(unicodeType)) { - isUnicode = true; - } - if (!quotesDetected) { - quotes = PyStringLiteralUtil.getQuotes(expression.getText()); - quotesDetected = true; - } - String value = ((PyStringLiteralExpression)expression).getStringValue(); - if (!useFormatMethod) { - value = value.replace("%", "%%"); - } - stringLiteral.append(escaper.apply(value)); - } - else { - addParamToString(stringLiteral, paramCount, useFormatMethod); - parameters.add(expression.getText()); - ++paramCount; - } - } - if (quotes == null) { - quotes = Pair.create("\"", "\""); - } - stringLiteral.insert(0, quotes.getFirst()); - if (isUnicode && !quotes.getFirst().toLowerCase().contains("u")) { - stringLiteral.insert(0, "u"); - } - stringLiteral.append(quotes.getSecond()); - - PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - - if (!parameters.isEmpty()) { - if (useFormatMethod) { - stringLiteral.append(".format(").append(StringUtil.join(parameters, ",")).append(")"); - - } - else { - final String paramString = parameters.size() > 1 ? "(" + StringUtil.join(parameters, ",") + ")" : StringUtil.join(parameters, ","); - stringLiteral.append(" % ").append(paramString); - } - final PyExpression expression = - elementGenerator.createFromText(LanguageLevel.getDefault(), PyExpressionStatement.class, stringLiteral.toString()).getExpression(); - element.replace(expression); - } - else { - PyStringLiteralExpression stringLiteralExpression = elementGenerator.createStringLiteralAlreadyEscaped(stringLiteral.toString()); - element.replace(stringLiteralExpression); + if (element == null) { + return; + } + final LanguageLevel languageLevel = LanguageLevel.forElement(element); + final boolean useFormatMethod = languageLevel.isAtLeast(LanguageLevel.PYTHON27); + + NotNullFunction escaper = consulo.ide.impl.idea.openapi.util.text.StringUtil.escaper(false, "\"\'\\"); + StringBuilder stringLiteral = new StringBuilder(); + List parameters = new ArrayList<>(); + Pair quotes = Pair.create("\"", "\""); + boolean quotesDetected = false; + final TypeEvalContext context = TypeEvalContext.userInitiated(file.getProject(), file); + int paramCount = 0; + boolean isUnicode = false; + final PyClassTypeImpl unicodeType = PyBuiltinCache.getInstance(element).getObjectType("unicode"); + + for (PyExpression expression : getSimpleExpressions((PyBinaryExpression) element)) { + if (expression instanceof PyStringLiteralExpression) { + final PyType type = context.getType(expression); + if (type != null && type.equals(unicodeType)) { + isUnicode = true; + } + if (!quotesDetected) { + quotes = PyStringLiteralUtil.getQuotes(expression.getText()); + quotesDetected = true; + } + String value = ((PyStringLiteralExpression) expression).getStringValue(); + if (!useFormatMethod) { + value = value.replace("%", "%%"); + } + stringLiteral.append(escaper.apply(value)); + } + else { + addParamToString(stringLiteral, paramCount, useFormatMethod); + parameters.add(expression.getText()); + ++paramCount; + } + } + if (quotes == null) { + quotes = Pair.create("\"", "\""); + } + stringLiteral.insert(0, quotes.getFirst()); + if (isUnicode && !quotes.getFirst().toLowerCase().contains("u")) { + stringLiteral.insert(0, "u"); + } + stringLiteral.append(quotes.getSecond()); + + PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + + if (!parameters.isEmpty()) { + if (useFormatMethod) { + stringLiteral.append(".format(").append(StringUtil.join(parameters, ",")).append(")"); + + } + else { + final String paramString = + parameters.size() > 1 ? "(" + StringUtil.join(parameters, ",") + ")" : StringUtil.join(parameters, ","); + stringLiteral.append(" % ").append(paramString); + } + final PyExpression expression = + elementGenerator.createFromText(LanguageLevel.getDefault(), PyExpressionStatement.class, stringLiteral.toString()) + .getExpression(); + element.replace(expression); + } + else { + PyStringLiteralExpression stringLiteralExpression = + elementGenerator.createStringLiteralAlreadyEscaped(stringLiteral.toString()); + element.replace(stringLiteralExpression); + } } - } - private static void addParamToString(StringBuilder stringLiteral, int i, boolean useFormatMethod) { - if (useFormatMethod) { - stringLiteral.append("{").append(i).append("}"); - } - else { - stringLiteral.append("%s"); + private static void addParamToString(StringBuilder stringLiteral, int i, boolean useFormatMethod) { + if (useFormatMethod) { + stringLiteral.append("{").append(i).append("}"); + } + else { + stringLiteral.append("%s"); + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyTransformConditionalExpressionIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyTransformConditionalExpressionIntention.java index e2aadf27..48809a79 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyTransformConditionalExpressionIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyTransformConditionalExpressionIntention.java @@ -13,82 +13,75 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.codeInsight.intentions; -import jakarta.annotation.Nonnull; - -import consulo.language.editor.intention.BaseIntentionAction; +import com.jetbrains.python.psi.*; import consulo.codeEditor.Editor; -import consulo.project.Project; +import consulo.language.editor.intention.BaseIntentionAction; import consulo.language.psi.PsiFile; import consulo.language.psi.util.PsiTreeUtil; -import com.jetbrains.python.impl.PyBundle; -import com.jetbrains.python.psi.*; import consulo.language.util.IncorrectOperationException; +import consulo.localize.LocalizeValue; +import consulo.project.Project; +import consulo.python.impl.localize.PyLocalize; +import jakarta.annotation.Nonnull; /** - * User: catherine - * Intention to transform conditional expression into if/else statement - * For instance, - * - * x = a if cond else b + * Intention to transform conditional expression into if/else statement. * + *

For instance, + * x = a if cond else b * into: - * - * if cond: - * x = a + * if cond: + * x = a * else: - * x = b + * x = b

+ * + * @author catherine */ public class PyTransformConditionalExpressionIntention extends BaseIntentionAction { - @Nonnull - public String getFamilyName() { - return PyBundle.message("INTN.transform.into.if.else.statement"); - } - - @Nonnull - public String getText() { - return PyBundle.message("INTN.transform.into.if.else.statement"); - } - - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { - if (!(file instanceof PyFile)) { - return false; + @Nonnull + @Override + public LocalizeValue getText() { + return PyLocalize.intnTransformIntoIfElseStatement(); } - PyAssignmentStatement expression = - PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyAssignmentStatement.class); - if (expression != null && expression.getAssignedValue() instanceof PyConditionalExpression) { - return true; + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (!(file instanceof PyFile)) { + return false; + } + + PyAssignmentStatement expression = + PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyAssignmentStatement.class); + if (expression != null && expression.getAssignedValue() instanceof PyConditionalExpression) { + return true; + } + return false; } - return false; - } - public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException - { - final PyAssignmentStatement assignmentStatement = - PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyAssignmentStatement.class); - assert assignmentStatement != null; - final PyExpression assignedValue = - assignmentStatement.getAssignedValue(); - if (assignedValue instanceof PyConditionalExpression) { - final PyConditionalExpression expression = (PyConditionalExpression)assignedValue; - final PyExpression condition = expression.getCondition(); - final PyExpression falsePart = expression.getFalsePart(); - if (condition != null && falsePart != null) { - final String truePartText = expression.getTruePart().getText(); - final PyExpression leftHandSideExpression = assignmentStatement.getLeftHandSideExpression(); - if (leftHandSideExpression != null) { - final String targetText = leftHandSideExpression.getText(); - final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - final String text = "if " + condition.getText() + ":\n\t" + targetText + " = " + truePartText + public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + final PyAssignmentStatement assignmentStatement = + PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyAssignmentStatement.class); + assert assignmentStatement != null; + final PyExpression assignedValue = + assignmentStatement.getAssignedValue(); + if (assignedValue instanceof PyConditionalExpression) { + final PyConditionalExpression expression = (PyConditionalExpression) assignedValue; + final PyExpression condition = expression.getCondition(); + final PyExpression falsePart = expression.getFalsePart(); + if (condition != null && falsePart != null) { + final String truePartText = expression.getTruePart().getText(); + final PyExpression leftHandSideExpression = assignmentStatement.getLeftHandSideExpression(); + if (leftHandSideExpression != null) { + final String targetText = leftHandSideExpression.getText(); + final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + final String text = "if " + condition.getText() + ":\n\t" + targetText + " = " + truePartText + "\nelse:\n\t" + targetText + " = " + falsePart.getText(); - final PyIfStatement ifStatement = elementGenerator.createFromText(LanguageLevel.forElement(expression), PyIfStatement.class, text); - assignmentStatement.replace(ifStatement); + final PyIfStatement ifStatement = + elementGenerator.createFromText(LanguageLevel.forElement(expression), PyIfStatement.class, text); + assignmentStatement.replace(ifStatement); + } + } } - } } - } - } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyYieldFromIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyYieldFromIntention.java index f361ddb3..5346d0e0 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyYieldFromIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyYieldFromIntention.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.codeInsight.intentions; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.psi.*; import consulo.codeEditor.Editor; import consulo.language.editor.intention.BaseIntentionAction; @@ -24,8 +22,9 @@ import consulo.language.psi.PsiFile; import consulo.language.psi.util.PsiTreeUtil; import consulo.language.util.IncorrectOperationException; +import consulo.localize.LocalizeValue; import consulo.project.Project; - +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -33,87 +32,88 @@ * @author vlan */ public class PyYieldFromIntention extends BaseIntentionAction { - @Nonnull - @Override - public String getText() { - return PyBundle.message("INTN.yield.from"); - } + @Nonnull + @Override + public LocalizeValue getText() { + return PyLocalize.intnYieldFrom(); + } - @Override - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { - if (LanguageLevel.forElement(file).isAtLeast(LanguageLevel.PYTHON33)) { - final PyForStatement forLoop = findForStatementAtCaret(editor, file); - if (forLoop != null) { - final PyTargetExpression forTarget = findSingleForLoopTarget(forLoop); - final PyReferenceExpression yieldValue = findSingleYieldValue(forLoop); - if (forTarget != null && yieldValue != null) { - final String targetName = forTarget.getName(); - if (targetName != null && targetName.equals(yieldValue.getName())) { - return true; - } + @Override + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (LanguageLevel.forElement(file).isAtLeast(LanguageLevel.PYTHON33)) { + final PyForStatement forLoop = findForStatementAtCaret(editor, file); + if (forLoop != null) { + final PyTargetExpression forTarget = findSingleForLoopTarget(forLoop); + final PyReferenceExpression yieldValue = findSingleYieldValue(forLoop); + if (forTarget != null && yieldValue != null) { + final String targetName = forTarget.getName(); + if (targetName != null && targetName.equals(yieldValue.getName())) { + return true; + } + } + } } - } + return false; } - return false; - } - @Override - public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - final PyForStatement forLoop = findForStatementAtCaret(editor, file); - if (forLoop != null) { - final PyExpression source = forLoop.getForPart().getSource(); - if (source != null) { - final PyElementGenerator generator = PyElementGenerator.getInstance(project); - final String text = "yield from foo"; - final PyExpressionStatement exprStmt = generator.createFromText(LanguageLevel.forElement(file), PyExpressionStatement.class, text); - final PyExpression expr = exprStmt.getExpression(); - if (expr instanceof PyYieldExpression) { - final PyExpression yieldValue = ((PyYieldExpression)expr).getExpression(); - if (yieldValue != null) { - yieldValue.replace(source); - forLoop.replace(exprStmt); - } + @Override + public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + final PyForStatement forLoop = findForStatementAtCaret(editor, file); + if (forLoop != null) { + final PyExpression source = forLoop.getForPart().getSource(); + if (source != null) { + final PyElementGenerator generator = PyElementGenerator.getInstance(project); + final String text = "yield from foo"; + final PyExpressionStatement exprStmt = + generator.createFromText(LanguageLevel.forElement(file), PyExpressionStatement.class, text); + final PyExpression expr = exprStmt.getExpression(); + if (expr instanceof PyYieldExpression) { + final PyExpression yieldValue = ((PyYieldExpression) expr).getExpression(); + if (yieldValue != null) { + yieldValue.replace(source); + forLoop.replace(exprStmt); + } + } + } } - } } - } - @Nullable - private static PyForStatement findForStatementAtCaret(@Nonnull Editor editor, @Nonnull PsiFile file) { - final PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset()); - return PsiTreeUtil.getParentOfType(elementAtCaret, PyForStatement.class); - } + @Nullable + private static PyForStatement findForStatementAtCaret(@Nonnull Editor editor, @Nonnull PsiFile file) { + final PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset()); + return PsiTreeUtil.getParentOfType(elementAtCaret, PyForStatement.class); + } - @Nullable - private static PyTargetExpression findSingleForLoopTarget(@Nonnull PyForStatement forLoop) { - final PyForPart forPart = forLoop.getForPart(); - final PyExpression forTarget = forPart.getTarget(); - if (forTarget instanceof PyTargetExpression) { - return (PyTargetExpression)forTarget; + @Nullable + private static PyTargetExpression findSingleForLoopTarget(@Nonnull PyForStatement forLoop) { + final PyForPart forPart = forLoop.getForPart(); + final PyExpression forTarget = forPart.getTarget(); + if (forTarget instanceof PyTargetExpression) { + return (PyTargetExpression) forTarget; + } + return null; } - return null; - } - @Nullable - private static PyReferenceExpression findSingleYieldValue(@Nonnull PyForStatement forLoop) { - final PyForPart forPart = forLoop.getForPart(); - final PyStatementList stmtList = forPart.getStatementList(); - if (stmtList != null && forLoop.getElsePart() == null) { - final PyStatement[] statements = stmtList.getStatements(); - if (statements.length == 1) { - final PyStatement firstStmt = statements[0]; - if (firstStmt instanceof PyExpressionStatement) { - final PyExpression firstExpr = ((PyExpressionStatement)firstStmt).getExpression(); - if (firstExpr instanceof PyYieldExpression) { - final PyYieldExpression yieldExpr = (PyYieldExpression)firstExpr; - final PyExpression yieldValue = yieldExpr.getExpression(); - if (yieldValue instanceof PyReferenceExpression) { - return (PyReferenceExpression)yieldValue; + @Nullable + private static PyReferenceExpression findSingleYieldValue(@Nonnull PyForStatement forLoop) { + final PyForPart forPart = forLoop.getForPart(); + final PyStatementList stmtList = forPart.getStatementList(); + if (stmtList != null && forLoop.getElsePart() == null) { + final PyStatement[] statements = stmtList.getStatements(); + if (statements.length == 1) { + final PyStatement firstStmt = statements[0]; + if (firstStmt instanceof PyExpressionStatement) { + final PyExpression firstExpr = ((PyExpressionStatement) firstStmt).getExpression(); + if (firstExpr instanceof PyYieldExpression) { + final PyYieldExpression yieldExpr = (PyYieldExpression) firstExpr; + final PyExpression yieldValue = yieldExpr.getExpression(); + if (yieldValue instanceof PyReferenceExpression) { + return (PyReferenceExpression) yieldValue; + } + } + } } - } } - } + return null; } - return null; - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/ReplaceListComprehensionWithForIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/ReplaceListComprehensionWithForIntention.java index 8499299e..9bc7361d 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/ReplaceListComprehensionWithForIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/ReplaceListComprehensionWithForIntention.java @@ -15,128 +15,120 @@ */ package com.jetbrains.python.impl.codeInsight.intentions; -import java.util.List; - -import jakarta.annotation.Nonnull; - +import com.jetbrains.python.impl.psi.impl.PyStatementListImpl; +import com.jetbrains.python.psi.*; import consulo.codeEditor.Editor; -import consulo.project.Project; import consulo.language.psi.PsiElement; import consulo.language.psi.PsiFile; import consulo.language.psi.util.PsiTreeUtil; -import com.jetbrains.python.impl.PyBundle; -import com.jetbrains.python.psi.*; -import com.jetbrains.python.impl.psi.impl.PyStatementListImpl; import consulo.language.util.IncorrectOperationException; +import consulo.localize.LocalizeValue; +import consulo.project.Project; +import consulo.python.impl.localize.PyLocalize; +import jakarta.annotation.Nonnull; + +import java.util.List; /** - * User: catherine + * @author catherine */ -public class ReplaceListComprehensionWithForIntention extends PyBaseIntentionAction -{ - @Nonnull - public String getText() - { - return PyBundle.message("INTN.replace.list.comprehensions.with.for"); - } - - @Nonnull - public String getFamilyName() - { - return PyBundle.message("INTN.replace.list.comprehensions.with.for"); - } +public class ReplaceListComprehensionWithForIntention extends PyBaseIntentionAction { + @Nonnull + @Override + public LocalizeValue getText() { + return PyLocalize.intnReplaceListComprehensionsWithFor(); + } - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) - { - if(!(file instanceof PyFile)) - { - return false; - } + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (!(file instanceof PyFile)) { + return false; + } - PyListCompExpression expression = PsiTreeUtil.getTopmostParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyListCompExpression.class); - if(expression == null) - { - return false; - } - if(expression.getComponents().isEmpty()) - { - return false; - } - PsiElement parent = expression.getParent(); - if(parent instanceof PyAssignmentStatement || parent instanceof PyPrintStatement) - { - return true; - } - return false; - } + PyListCompExpression expression = + PsiTreeUtil.getTopmostParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyListCompExpression.class); + if (expression == null) { + return false; + } + if (expression.getComponents().isEmpty()) { + return false; + } + PsiElement parent = expression.getParent(); + if (parent instanceof PyAssignmentStatement || parent instanceof PyPrintStatement) { + return true; + } + return false; + } - @Override - public void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException - { - PyListCompExpression expression = PsiTreeUtil.getTopmostParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyListCompExpression.class); - if(expression == null) - { - return; - } - PsiElement parent = expression.getParent(); - PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + @Override + public void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + PyListCompExpression expression = + PsiTreeUtil.getTopmostParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyListCompExpression.class); + if (expression == null) { + return; + } + PsiElement parent = expression.getParent(); + PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - if(parent instanceof PyAssignmentStatement) - { - final PsiElement leftExpr = ((PyAssignmentStatement) parent).getLeftHandSideExpression(); - if(leftExpr == null) - { - return; - } - PyAssignmentStatement initAssignment = elementGenerator.createFromText(LanguageLevel.forElement(expression), PyAssignmentStatement.class, leftExpr.getText() + " = []"); - PyForStatement forStatement = createForLoop(expression, elementGenerator, leftExpr.getText() + ".append(" + expression.getResultExpression().getText() + ")"); + if (parent instanceof PyAssignmentStatement) { + final PsiElement leftExpr = ((PyAssignmentStatement) parent).getLeftHandSideExpression(); + if (leftExpr == null) { + return; + } + PyAssignmentStatement initAssignment = elementGenerator.createFromText( + LanguageLevel.forElement(expression), + PyAssignmentStatement.class, + leftExpr.getText() + " = []" + ); + PyForStatement forStatement = createForLoop( + expression, + elementGenerator, + leftExpr.getText() + ".append(" + expression.getResultExpression().getText() + ")" + ); - PyStatementList stList = new PyStatementListImpl(initAssignment.getNode()); - stList.add(initAssignment); - stList.add(forStatement); - stList.getStatements()[0].delete(); - parent.replace(stList); + PyStatementList stList = new PyStatementListImpl(initAssignment.getNode()); + stList.add(initAssignment); + stList.add(forStatement); + stList.getStatements()[0].delete(); + parent.replace(stList); - } - else if(parent instanceof PyPrintStatement) - { - PyForStatement forStatement = createForLoop(expression, elementGenerator, "print " + "(" + expression.getResultExpression().getText() + ")"); - parent.replace(forStatement); - } - } + } + else if (parent instanceof PyPrintStatement) { + PyForStatement forStatement = + createForLoop(expression, elementGenerator, "print " + "(" + expression.getResultExpression().getText() + ")"); + parent.replace(forStatement); + } + } - private static PyForStatement createForLoop(final PyListCompExpression expression, final PyElementGenerator elementGenerator, final String result) - { - final List components = expression.getComponents(); - final StringBuilder stringBuilder = new StringBuilder(); - int slashNum = 1; - for(PyComprehensionComponent component : components) - { - if(component instanceof PyComprehensionForComponent) - { - stringBuilder.append("for "); - stringBuilder.append(((PyComprehensionForComponent) component).getIteratorVariable().getText()); - stringBuilder.append(" in "); - stringBuilder.append(((PyComprehensionForComponent) component).getIteratedList().getText()); - stringBuilder.append(":\n"); - } - if(component instanceof PyComprehensionIfComponent) - { - final PyExpression test = ((PyComprehensionIfComponent) component).getTest(); - if(test != null) - { - stringBuilder.append("if "); - stringBuilder.append(test.getText()); - stringBuilder.append(":\n"); - } - } - for(int i = 0; i != slashNum; ++i) - { - stringBuilder.append("\t"); - } - ++slashNum; - } - stringBuilder.append(result); - return elementGenerator.createFromText(LanguageLevel.forElement(expression), PyForStatement.class, stringBuilder.toString()); - } + private static PyForStatement createForLoop( + final PyListCompExpression expression, + final PyElementGenerator elementGenerator, + final String result + ) { + final List components = expression.getComponents(); + final StringBuilder stringBuilder = new StringBuilder(); + int slashNum = 1; + for (PyComprehensionComponent component : components) { + if (component instanceof PyComprehensionForComponent) { + stringBuilder.append("for "); + stringBuilder.append(((PyComprehensionForComponent) component).getIteratorVariable().getText()); + stringBuilder.append(" in "); + stringBuilder.append(((PyComprehensionForComponent) component).getIteratedList().getText()); + stringBuilder.append(":\n"); + } + if (component instanceof PyComprehensionIfComponent) { + final PyExpression test = ((PyComprehensionIfComponent) component).getTest(); + if (test != null) { + stringBuilder.append("if "); + stringBuilder.append(test.getText()); + stringBuilder.append(":\n"); + } + } + for (int i = 0; i != slashNum; ++i) { + stringBuilder.append("\t"); + } + ++slashNum; + } + stringBuilder.append(result); + return elementGenerator.createFromText(LanguageLevel.forElement(expression), PyForStatement.class, stringBuilder.toString()); + } } \ No newline at end of file diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/SpecifyTypeInDocstringIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/SpecifyTypeInDocstringIntention.java index 883b6645..f787d126 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/SpecifyTypeInDocstringIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/SpecifyTypeInDocstringIntention.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.impl.codeInsight.intentions; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.debugger.PySignature; import com.jetbrains.python.impl.debugger.PySignatureCacheManager; @@ -30,101 +29,99 @@ import consulo.language.psi.PsiReference; import consulo.language.psi.util.PsiTreeUtil; import consulo.language.util.IncorrectOperationException; +import consulo.localize.LocalizeValue; import consulo.project.Project; +import consulo.python.impl.localize.PyLocalize; import consulo.util.lang.ObjectUtil; import consulo.util.lang.StringUtil; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; /** - * User: ktisha - *

- * Helps to specify type + * Helps to specify type. + * + * @author ktisha */ public class SpecifyTypeInDocstringIntention extends TypeIntention { - private String myText = PyBundle.message("INTN.specify.type"); - - @Nonnull - public String getText() { - return myText; - } + @Nonnull + private LocalizeValue myText = PyLocalize.intnSpecifyType(); - @Nonnull - public String getFamilyName() { - return PyBundle.message("INTN.specify.type"); - } + @Nonnull + @Override + public LocalizeValue getText() { + return myText; + } - @Override - public void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - final PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset()); - final PyExpression problemElement = getProblemElement(elementAt); - final PsiReference reference = problemElement == null ? null : problemElement.getReference(); + @Override + public void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + final PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset()); + final PyExpression problemElement = getProblemElement(elementAt); + final PsiReference reference = problemElement == null ? null : problemElement.getReference(); - final PsiElement resolved = reference != null ? reference.resolve() : null; - final PyNamedParameter parameter = getParameter(problemElement, resolved); + final PsiElement resolved = reference != null ? reference.resolve() : null; + final PyNamedParameter parameter = getParameter(problemElement, resolved); - final PyCallable callable; - if (parameter != null) { - callable = PsiTreeUtil.getParentOfType(parameter, PyFunction.class); - } - else { - callable = getCallable(elementAt); - } - if (callable instanceof PyFunction) { - generateDocstring(parameter, (PyFunction)callable); + final PyCallable callable; + if (parameter != null) { + callable = PsiTreeUtil.getParentOfType(parameter, PyFunction.class); + } + else { + callable = getCallable(elementAt); + } + if (callable instanceof PyFunction) { + generateDocstring(parameter, (PyFunction) callable); + } } - } - private static void generateDocstring(@Nullable PyNamedParameter param, @Nonnull PyFunction pyFunction) { - if (!DocStringUtil.ensureNotPlainDocstringFormat(pyFunction)) { - return; - } + private static void generateDocstring(@Nullable PyNamedParameter param, @Nonnull PyFunction pyFunction) { + if (!DocStringUtil.ensureNotPlainDocstringFormat(pyFunction)) { + return; + } - final PyDocstringGenerator docstringGenerator = PyDocstringGenerator.forDocStringOwner(pyFunction); - String type = PyNames.OBJECT; - if (param != null) { - final String paramName = StringUtil.notNullize(param.getName()); - final PySignature signature = PySignatureCacheManager.getInstance(pyFunction.getProject()).findSignature(pyFunction); - if (signature != null) { - type = ObjectUtil.chooseNotNull(signature.getArgTypeQualifiedName(paramName), type); - } - docstringGenerator.withParamTypedByName(param, type); - } - else { - final PySignature signature = PySignatureCacheManager.getInstance(pyFunction.getProject()).findSignature(pyFunction); - if (signature != null) { - type = ObjectUtil.chooseNotNull(signature.getReturnTypeQualifiedName(), type); - } - docstringGenerator.withReturnValue(type); - } + final PyDocstringGenerator docstringGenerator = PyDocstringGenerator.forDocStringOwner(pyFunction); + String type = PyNames.OBJECT; + if (param != null) { + final String paramName = StringUtil.notNullize(param.getName()); + final PySignature signature = PySignatureCacheManager.getInstance(pyFunction.getProject()).findSignature(pyFunction); + if (signature != null) { + type = ObjectUtil.chooseNotNull(signature.getArgTypeQualifiedName(paramName), type); + } + docstringGenerator.withParamTypedByName(param, type); + } + else { + final PySignature signature = PySignatureCacheManager.getInstance(pyFunction.getProject()).findSignature(pyFunction); + if (signature != null) { + type = ObjectUtil.chooseNotNull(signature.getReturnTypeQualifiedName(), type); + } + docstringGenerator.withReturnValue(type); + } - docstringGenerator.addFirstEmptyLine().buildAndInsert(); - docstringGenerator.startTemplate(); - } + docstringGenerator.addFirstEmptyLine().buildAndInsert(); + docstringGenerator.startTemplate(); + } - @Override - protected void updateText(boolean isReturn) { - myText = PyBundle.message(isReturn ? "INTN.specify.return.type" : "INTN.specify.type"); - } + @Override + protected void updateText(boolean isReturn) { + myText = isReturn ? PyLocalize.intnSpecifyReturnType() : PyLocalize.intnSpecifyType(); + } - @Override - protected boolean isParamTypeDefined(@Nonnull PyParameter parameter) { - final PyFunction pyFunction = PsiTreeUtil.getParentOfType(parameter, PyFunction.class); - if (pyFunction != null) { - final StructuredDocString structuredDocString = pyFunction.getStructuredDocString(); - if (structuredDocString == null) { + @Override + protected boolean isParamTypeDefined(@Nonnull PyParameter parameter) { + final PyFunction pyFunction = PsiTreeUtil.getParentOfType(parameter, PyFunction.class); + if (pyFunction != null) { + final StructuredDocString structuredDocString = pyFunction.getStructuredDocString(); + if (structuredDocString == null) { + return false; + } + final Substring typeSub = structuredDocString.getParamTypeSubstring(StringUtil.notNullize(parameter.getName())); + return typeSub != null && !typeSub.isEmpty(); + } return false; - } - final Substring typeSub = structuredDocString.getParamTypeSubstring(StringUtil.notNullize(parameter.getName())); - return typeSub != null && !typeSub.isEmpty(); } - return false; - } - @Override - protected boolean isReturnTypeDefined(@Nonnull PyFunction function) { - final StructuredDocString structuredDocString = function.getStructuredDocString(); - return structuredDocString != null && structuredDocString.getReturnType() != null; - } + @Override + protected boolean isReturnTypeDefined(@Nonnull PyFunction function) { + final StructuredDocString structuredDocString = function.getStructuredDocString(); + return structuredDocString != null && structuredDocString.getReturnType() != null; + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java index 5d1d0dd1..6ec85717 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/SpecifyTypeInPy3AnnotationsIntention.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.impl.codeInsight.intentions; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.debugger.PySignature; @@ -38,194 +37,195 @@ import consulo.language.psi.PsiReference; import consulo.language.psi.util.PsiTreeUtil; import consulo.language.util.IncorrectOperationException; +import consulo.localize.LocalizeValue; import consulo.navigation.OpenFileDescriptor; import consulo.navigation.OpenFileDescriptorFactory; import consulo.project.Project; +import consulo.python.impl.localize.PyLocalize; import consulo.util.lang.ObjectUtil; import consulo.util.lang.StringUtil; - import jakarta.annotation.Nonnull; /** - * User: ktisha - *

- * Helps to specify type in annotations in python3 + * Helps to specify type in annotations in python3. + * + * @author ktisha */ public class SpecifyTypeInPy3AnnotationsIntention extends TypeIntention { - private String myText = PyBundle.message("INTN.specify.type.in.annotation"); - - @Nonnull - public String getText() { - return myText; - } - - @Nonnull - public String getFamilyName() { - return PyBundle.message("INTN.specify.type.in.annotation"); - } - - @Override - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { - if (!LanguageLevel.forElement(file).isPy3K()) { - return false; + @Nonnull + private LocalizeValue myText = PyLocalize.intnSpecifyTypeInAnnotation(); + + @Nonnull + @Override + public LocalizeValue getText() { + return myText; } - return super.isAvailable(project, editor, file); - } - @Override - public void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - final PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset()); - final PyExpression problemElement = getProblemElement(elementAt); - final PsiReference reference = problemElement == null ? null : problemElement.getReference(); + @Override + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (!LanguageLevel.forElement(file).isPy3K()) { + return false; + } + return super.isAvailable(project, editor, file); + } - final PsiElement resolved = reference != null ? reference.resolve() : null; - final PyNamedParameter parameter = getParameter(problemElement, resolved); + @Override + public void doInvoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + final PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset()); + final PyExpression problemElement = getProblemElement(elementAt); + final PsiReference reference = problemElement == null ? null : problemElement.getReference(); - if (parameter != null) { - annotateParameter(project, editor, parameter, true); - } - else { - PyCallable callable = getCallable(elementAt); - if (callable instanceof PyFunction) { - annotateReturnType(project, (PyFunction)callable, true); - } + final PsiElement resolved = reference != null ? reference.resolve() : null; + final PyNamedParameter parameter = getParameter(problemElement, resolved); + + if (parameter != null) { + annotateParameter(project, editor, parameter, true); + } + else { + PyCallable callable = getCallable(elementAt); + if (callable instanceof PyFunction) { + annotateReturnType(project, (PyFunction) callable, true); + } + } } - } - static PyNamedParameter annotateParameter(Project project, Editor editor, @Nonnull PyNamedParameter parameter, boolean createTemplate) { - final PyExpression defaultParamValue = parameter.getDefaultValue(); + static PyNamedParameter annotateParameter(Project project, Editor editor, @Nonnull PyNamedParameter parameter, boolean createTemplate) { + final PyExpression defaultParamValue = parameter.getDefaultValue(); - final String paramName = StringUtil.notNullize(parameter.getName()); - final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); + final String paramName = StringUtil.notNullize(parameter.getName()); + final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); - final String defaultParamText = defaultParamValue == null ? null : defaultParamValue.getText(); + final String defaultParamText = defaultParamValue == null ? null : defaultParamValue.getText(); - String paramType = parameterType(parameter); + String paramType = parameterType(parameter); - final PyNamedParameter namedParameter = - elementGenerator.createParameter(paramName, defaultParamText, paramType, LanguageLevel.forElement(parameter)); - assert namedParameter != null; - parameter = (PyNamedParameter)parameter.replace(namedParameter); - parameter = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(parameter); - editor.getCaretModel().moveToOffset(parameter.getTextOffset()); - final PyAnnotation annotation = parameter.getAnnotation(); - if (annotation != null && createTemplate) { - final PyExpression annotationValue = annotation.getValue(); + final PyNamedParameter namedParameter = + elementGenerator.createParameter(paramName, defaultParamText, paramType, LanguageLevel.forElement(parameter)); + assert namedParameter != null; + parameter = (PyNamedParameter) parameter.replace(namedParameter); + parameter = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(parameter); + editor.getCaretModel().moveToOffset(parameter.getTextOffset()); + final PyAnnotation annotation = parameter.getAnnotation(); + if (annotation != null && createTemplate) { + final PyExpression annotationValue = annotation.getValue(); - final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(parameter); - assert annotationValue != null : "Generated parameter must have annotation"; - final int replacementStart = annotation.getStartOffsetInParent() + annotationValue.getStartOffsetInParent(); - builder.replaceRange(TextRange.create(replacementStart, replacementStart + annotationValue.getTextLength()), paramType); - final Template template = builder.buildInlineTemplate(); - TemplateManager.getInstance(project).startTemplate(editor, template); - } + final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(parameter); + assert annotationValue != null : "Generated parameter must have annotation"; + final int replacementStart = annotation.getStartOffsetInParent() + annotationValue.getStartOffsetInParent(); + builder.replaceRange(TextRange.create(replacementStart, replacementStart + annotationValue.getTextLength()), paramType); + final Template template = builder.buildInlineTemplate(); + TemplateManager.getInstance(project).startTemplate(editor, template); + } - return parameter; - } + return parameter; + } - static String parameterType(PyParameter parameter) { - String paramType = PyNames.OBJECT; + static String parameterType(PyParameter parameter) { + String paramType = PyNames.OBJECT; - PyFunction function = PsiTreeUtil.getParentOfType(parameter, PyFunction.class); - if (function != null) { - final PySignature signature = PySignatureCacheManager.getInstance(parameter.getProject()).findSignature(function); - String parameterName = parameter.getName(); - if (signature != null && parameterName != null) { - paramType = ObjectUtil.chooseNotNull(signature.getArgTypeQualifiedName(parameterName), paramType); - } + PyFunction function = PsiTreeUtil.getParentOfType(parameter, PyFunction.class); + if (function != null) { + final PySignature signature = PySignatureCacheManager.getInstance(parameter.getProject()).findSignature(function); + String parameterName = parameter.getName(); + if (signature != null && parameterName != null) { + paramType = ObjectUtil.chooseNotNull(signature.getArgTypeQualifiedName(parameterName), paramType); + } + } + return paramType; } - return paramType; - } - static String returnType(@Nonnull PyFunction function) { - String returnType = PyNames.OBJECT; - final PySignature signature = PySignatureCacheManager.getInstance(function.getProject()).findSignature(function); - if (signature != null) { - returnType = ObjectUtil.chooseNotNull(signature.getReturnTypeQualifiedName(), returnType); + static String returnType(@Nonnull PyFunction function) { + String returnType = PyNames.OBJECT; + final PySignature signature = PySignatureCacheManager.getInstance(function.getProject()).findSignature(function); + if (signature != null) { + returnType = ObjectUtil.chooseNotNull(signature.getReturnTypeQualifiedName(), returnType); + } + return returnType; } - return returnType; - } - - public static PyExpression annotateReturnType(Project project, PyFunction function, boolean createTemplate) { - String returnType = returnType(function); - - final String annotationText = "-> " + returnType; - - final PsiDocumentManager manager = PsiDocumentManager.getInstance(project); - Document documentWithCallable = manager.getDocument(function.getContainingFile()); - if (documentWithCallable != null) { - try { - manager.doPostponedOperationsAndUnblockDocument(documentWithCallable); - final PyAnnotation oldAnnotation = function.getAnnotation(); - if (oldAnnotation != null) { - final TextRange oldRange = oldAnnotation.getTextRange(); - documentWithCallable.replaceString(oldRange.getStartOffset(), oldRange.getEndOffset(), annotationText); + + public static PyExpression annotateReturnType(Project project, PyFunction function, boolean createTemplate) { + String returnType = returnType(function); + + final String annotationText = "-> " + returnType; + + final PsiDocumentManager manager = PsiDocumentManager.getInstance(project); + Document documentWithCallable = manager.getDocument(function.getContainingFile()); + if (documentWithCallable != null) { + try { + manager.doPostponedOperationsAndUnblockDocument(documentWithCallable); + final PyAnnotation oldAnnotation = function.getAnnotation(); + if (oldAnnotation != null) { + final TextRange oldRange = oldAnnotation.getTextRange(); + documentWithCallable.replaceString(oldRange.getStartOffset(), oldRange.getEndOffset(), annotationText); + } + else { + final PsiElement prevElem = PyPsiUtils.getPrevNonCommentSibling(function.getStatementList(), true); + assert prevElem != null; + final TextRange range = prevElem.getTextRange(); + if (prevElem.getNode().getElementType() == PyTokenTypes.COLON) { + documentWithCallable.insertString(range.getStartOffset(), " " + annotationText); + } + else { + documentWithCallable.insertString(range.getEndOffset(), " " + annotationText + ":"); + } + } + } + finally { + manager.commitDocument(documentWithCallable); + } } - else { - final PsiElement prevElem = PyPsiUtils.getPrevNonCommentSibling(function.getStatementList(), true); - assert prevElem != null; - final TextRange range = prevElem.getTextRange(); - if (prevElem.getNode().getElementType() == PyTokenTypes.COLON) { - documentWithCallable.insertString(range.getStartOffset(), " " + annotationText); - } - else { - documentWithCallable.insertString(range.getEndOffset(), " " + annotationText + ":"); - } + + + function = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(function); + final PyAnnotation annotation = function.getAnnotation(); + assert annotation != null; + final PyExpression annotationValue = annotation.getValue(); + assert annotationValue != null : "Generated function must have annotation"; + + if (createTemplate) { + final int offset = annotationValue.getTextOffset(); + + final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(annotationValue); + builder.replaceRange(TextRange.create(0, returnType.length()), returnType); + final Template template = builder.buildInlineTemplate(); + final OpenFileDescriptor descriptor = + OpenFileDescriptorFactory.getInstance(project) + .builder(function.getContainingFile().getVirtualFile()) + .offset(offset) + .build(); + final Editor targetEditor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true); + if (targetEditor != null) { + targetEditor.getCaretModel().moveToOffset(offset); + TemplateManager.getInstance(project).startTemplate(targetEditor, template); + } } - } - finally { - manager.commitDocument(documentWithCallable); - } + return annotationValue; } - - function = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(function); - final PyAnnotation annotation = function.getAnnotation(); - assert annotation != null; - final PyExpression annotationValue = annotation.getValue(); - assert annotationValue != null : "Generated function must have annotation"; - - if (createTemplate) { - final int offset = annotationValue.getTextOffset(); - - final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(annotationValue); - builder.replaceRange(TextRange.create(0, returnType.length()), returnType); - final Template template = builder.buildInlineTemplate(); - final OpenFileDescriptor descriptor = - OpenFileDescriptorFactory.getInstance(project).builder(function.getContainingFile().getVirtualFile()).offset(offset).build(); - final Editor targetEditor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true); - if (targetEditor != null) { - targetEditor.getCaretModel().moveToOffset(offset); - TemplateManager.getInstance(project).startTemplate(targetEditor, template); - } + @Override + protected boolean isParamTypeDefined(PyParameter parameter) { + return isDefinedInAnnotation(parameter); } - return annotationValue; - } - @Override - protected boolean isParamTypeDefined(PyParameter parameter) { - return isDefinedInAnnotation(parameter); - } + private static boolean isDefinedInAnnotation(PyParameter parameter) { + if (LanguageLevel.forElement(parameter).isOlderThan(LanguageLevel.PYTHON30)) { + return false; + } + if (parameter instanceof PyNamedParameter && (((PyNamedParameter) parameter).getAnnotation() != null)) { + return true; + } + return false; + } - private static boolean isDefinedInAnnotation(PyParameter parameter) { - if (LanguageLevel.forElement(parameter).isOlderThan(LanguageLevel.PYTHON30)) { - return false; + @Override + protected boolean isReturnTypeDefined(@Nonnull PyFunction function) { + return function.getAnnotation() != null; } - if (parameter instanceof PyNamedParameter && (((PyNamedParameter)parameter).getAnnotation() != null)) { - return true; + + @Override + protected void updateText(boolean isReturn) { + myText = isReturn ? PyLocalize.intnSpecifyReturnTypeInAnnotation() : PyLocalize.intnSpecifyTypeInAnnotation(); } - return false; - } - - @Override - protected boolean isReturnTypeDefined(@Nonnull PyFunction function) { - return function.getAnnotation() != null; - } - - @Override - protected void updateText(boolean isReturn) { - myText = isReturn ? PyBundle.message("INTN.specify.return.type.in.annotation") : PyBundle.message("INTN.specify.type.in.annotation"); - } } \ No newline at end of file diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/TypeAssertionIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/TypeAssertionIntention.java index ab360d73..f36e1b75 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/TypeAssertionIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/TypeAssertionIntention.java @@ -16,7 +16,6 @@ package com.jetbrains.python.impl.codeInsight.intentions; import com.jetbrains.python.PyNames; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.psi.PyUtil; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.types.PyType; @@ -36,7 +35,6 @@ import consulo.language.util.IncorrectOperationException; import consulo.localize.LocalizeValue; import consulo.project.Project; - import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/TypeIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/TypeIntention.java index de2e170b..b57b4f1d 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/TypeIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/TypeIntention.java @@ -39,226 +39,181 @@ import com.jetbrains.python.psi.types.TypeEvalContext; /** - * User: ktisha - *

- * Common part for type specifying intentions + * Common part for type specifying intentions. + * + * @author ktisha */ -public abstract class TypeIntention extends PyBaseIntentionAction -{ - - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) - { - if(!(file instanceof PyFile) || file instanceof PyDocstringFile) - { - return false; - } - updateText(false); - - final PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset()); - if(elementAt == null) - { - return false; - } - if(isAvailableForParameter(project, elementAt)) - { - return true; - } - if(isAvailableForReturn(elementAt)) - { - updateText(true); - return true; - } - return false; - } - - private boolean isAvailableForParameter(Project project, PsiElement elementAt) - { - final PyExpression problemElement = getProblemElement(elementAt); - if(problemElement == null) - { - return false; - } - if(PsiTreeUtil.getParentOfType(problemElement, PyLambdaExpression.class) != null) - { - return false; - } - final PsiReference reference = problemElement.getReference(); - if(reference instanceof PsiPolyVariantReference) - { - final ResolveResult[] results = ((PsiPolyVariantReference) reference).multiResolve(false); - if(results.length != 1) - { - return false; - } - } - final VirtualFile virtualFile = problemElement.getContainingFile().getVirtualFile(); - if(virtualFile != null) - { - if(ProjectRootManager.getInstance(project).getFileIndex().isInLibraryClasses(virtualFile)) - { - return false; - } - } - final PsiElement resolved = reference != null ? reference.resolve() : null; - final PyParameter parameter = getParameter(problemElement, resolved); - - return parameter != null && !isParamTypeDefined(parameter); - } - - @Nullable - public static PyExpression getProblemElement(@Nullable PsiElement elementAt) - { - PyExpression problemElement = PsiTreeUtil.getParentOfType(elementAt, PyNamedParameter.class, PyReferenceExpression.class); - if(problemElement == null) - { - return null; - } - if(problemElement instanceof PyQualifiedExpression) - { - final PyExpression qualifier = ((PyQualifiedExpression) problemElement).getQualifier(); - if(qualifier != null && !qualifier.getText().equals(PyNames.CANONICAL_SELF)) - { - problemElement = qualifier; - } - } - return problemElement; - } - - protected abstract void updateText(boolean isReturn); - - protected boolean isParamTypeDefined(PyParameter parameter) - { - return false; - } - - @Nullable - protected static PyNamedParameter getParameter(PyExpression problemElement, PsiElement resolved) - { - PyNamedParameter parameter = as(problemElement, PyNamedParameter.class); - if(resolved instanceof PyNamedParameter) - { - parameter = (PyNamedParameter) resolved; - } - return parameter == null || parameter.isSelf() ? null : parameter; - } - - private boolean isAvailableForReturn(@Nonnull final PsiElement elementAt) - { - return resolvesToFunction(elementAt, new Function() - { - @Override - public Boolean apply(PyFunction input) - { - return !isReturnTypeDefined(input); - } - }); - } - - static boolean resolvesToFunction(@Nonnull PsiElement elementAt, Function isAvailableForFunction) - { - final PyFunction parentFunction = PsiTreeUtil.getParentOfType(elementAt, PyFunction.class); - if(parentFunction != null) - { - final ASTNode nameNode = parentFunction.getNameNode(); - if(nameNode != null) - { - final PsiElement prev = elementAt.getContainingFile().findElementAt(elementAt.getTextOffset() - 1); - if(nameNode.getPsi() == elementAt || nameNode.getPsi() == prev) - { - return isAvailableForFunction.apply(parentFunction); - } - } - } - - final PyCallExpression callExpression = getCallExpression(elementAt); - if(callExpression == null) - { - return false; - } - final PyExpression callee = callExpression.getCallee(); - if(callee == null) - { - return false; - } - final PsiReference reference = callee.getReference(); - if(reference instanceof PsiPolyVariantReference) - { - final ResolveResult[] results = ((PsiPolyVariantReference) reference).multiResolve(false); - for(int i = 0; i < results.length; i++) - { - if(results[i].getElement() instanceof PyFunction) - { - final PsiElement result = results[i].getElement(); - final PsiFile psiFile = result.getContainingFile(); - if(psiFile == null) - { - return false; - } - final VirtualFile virtualFile = psiFile.getVirtualFile(); - if(virtualFile != null) - { - if(ProjectRootManager.getInstance(psiFile.getProject()).getFileIndex().isInLibraryClasses(virtualFile)) - { - return false; - } - } - return isAvailableForFunction.apply((PyFunction) result); - } - } - } - return false; - } - - protected boolean isReturnTypeDefined(@Nonnull PyFunction function) - { - return false; - } - - @Nullable - static PyCallExpression getCallExpression(PsiElement elementAt) - { - final PyExpression problemElement = getProblemElement(elementAt); - if(problemElement != null) - { - PsiReference reference = problemElement.getReference(); - final PsiElement resolved = reference != null ? reference.resolve() : null; - if(resolved instanceof PyTargetExpression) - { - final PyExpression assignedValue = ((PyTargetExpression) resolved).findAssignedValue(); - if(assignedValue instanceof PyCallExpression) - { - return (PyCallExpression) assignedValue; - } - } - } - - PyAssignmentStatement assignmentStatement = PsiTreeUtil.getParentOfType(elementAt, PyAssignmentStatement.class); - if(assignmentStatement != null) - { - final PyExpression assignedValue = assignmentStatement.getAssignedValue(); - if(assignedValue instanceof PyCallExpression) - { - return (PyCallExpression) assignedValue; - } - } - return PsiTreeUtil.getParentOfType(elementAt, PyCallExpression.class, false); - } - - @Nullable - static PyCallable getCallable(PsiElement elementAt) - { - PyCallExpression callExpression = getCallExpression(elementAt); - - if(callExpression != null && elementAt != null) - { - final PyCallable callable = callExpression.resolveCalleeFunction(getResolveContext(elementAt)); - return callable == null ? PsiTreeUtil.getParentOfType(elementAt, PyFunction.class) : callable; - } - return PsiTreeUtil.getParentOfType(elementAt, PyFunction.class); - } - - protected static PyResolveContext getResolveContext(@Nonnull PsiElement origin) - { - return PyResolveContext.defaultContext().withTypeEvalContext(TypeEvalContext.codeAnalysis(origin.getProject(), origin.getContainingFile())); - } +public abstract class TypeIntention extends PyBaseIntentionAction { + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + if (!(file instanceof PyFile) || file instanceof PyDocstringFile) { + return false; + } + updateText(false); + + final PsiElement elementAt = PyUtil.findNonWhitespaceAtOffset(file, editor.getCaretModel().getOffset()); + if (elementAt == null) { + return false; + } + if (isAvailableForParameter(project, elementAt)) { + return true; + } + if (isAvailableForReturn(elementAt)) { + updateText(true); + return true; + } + return false; + } + + private boolean isAvailableForParameter(Project project, PsiElement elementAt) { + final PyExpression problemElement = getProblemElement(elementAt); + if (problemElement == null) { + return false; + } + if (PsiTreeUtil.getParentOfType(problemElement, PyLambdaExpression.class) != null) { + return false; + } + final PsiReference reference = problemElement.getReference(); + if (reference instanceof PsiPolyVariantReference) { + final ResolveResult[] results = ((PsiPolyVariantReference) reference).multiResolve(false); + if (results.length != 1) { + return false; + } + } + final VirtualFile virtualFile = problemElement.getContainingFile().getVirtualFile(); + if (virtualFile != null) { + if (ProjectRootManager.getInstance(project).getFileIndex().isInLibraryClasses(virtualFile)) { + return false; + } + } + final PsiElement resolved = reference != null ? reference.resolve() : null; + final PyParameter parameter = getParameter(problemElement, resolved); + + return parameter != null && !isParamTypeDefined(parameter); + } + + @Nullable + public static PyExpression getProblemElement(@Nullable PsiElement elementAt) { + PyExpression problemElement = PsiTreeUtil.getParentOfType(elementAt, PyNamedParameter.class, PyReferenceExpression.class); + if (problemElement == null) { + return null; + } + if (problemElement instanceof PyQualifiedExpression) { + final PyExpression qualifier = ((PyQualifiedExpression) problemElement).getQualifier(); + if (qualifier != null && !qualifier.getText().equals(PyNames.CANONICAL_SELF)) { + problemElement = qualifier; + } + } + return problemElement; + } + + protected abstract void updateText(boolean isReturn); + + protected boolean isParamTypeDefined(PyParameter parameter) { + return false; + } + + @Nullable + protected static PyNamedParameter getParameter(PyExpression problemElement, PsiElement resolved) { + PyNamedParameter parameter = as(problemElement, PyNamedParameter.class); + if (resolved instanceof PyNamedParameter) { + parameter = (PyNamedParameter) resolved; + } + return parameter == null || parameter.isSelf() ? null : parameter; + } + + private boolean isAvailableForReturn(@Nonnull final PsiElement elementAt) { + return resolvesToFunction(elementAt, new Function() { + @Override + public Boolean apply(PyFunction input) { + return !isReturnTypeDefined(input); + } + }); + } + + static boolean resolvesToFunction(@Nonnull PsiElement elementAt, Function isAvailableForFunction) { + final PyFunction parentFunction = PsiTreeUtil.getParentOfType(elementAt, PyFunction.class); + if (parentFunction != null) { + final ASTNode nameNode = parentFunction.getNameNode(); + if (nameNode != null) { + final PsiElement prev = elementAt.getContainingFile().findElementAt(elementAt.getTextOffset() - 1); + if (nameNode.getPsi() == elementAt || nameNode.getPsi() == prev) { + return isAvailableForFunction.apply(parentFunction); + } + } + } + + final PyCallExpression callExpression = getCallExpression(elementAt); + if (callExpression == null) { + return false; + } + final PyExpression callee = callExpression.getCallee(); + if (callee == null) { + return false; + } + final PsiReference reference = callee.getReference(); + if (reference instanceof PsiPolyVariantReference) { + final ResolveResult[] results = ((PsiPolyVariantReference) reference).multiResolve(false); + for (int i = 0; i < results.length; i++) { + if (results[i].getElement() instanceof PyFunction) { + final PsiElement result = results[i].getElement(); + final PsiFile psiFile = result.getContainingFile(); + if (psiFile == null) { + return false; + } + final VirtualFile virtualFile = psiFile.getVirtualFile(); + if (virtualFile != null) { + if (ProjectRootManager.getInstance(psiFile.getProject()).getFileIndex().isInLibraryClasses(virtualFile)) { + return false; + } + } + return isAvailableForFunction.apply((PyFunction) result); + } + } + } + return false; + } + + protected boolean isReturnTypeDefined(@Nonnull PyFunction function) { + return false; + } + + @Nullable + static PyCallExpression getCallExpression(PsiElement elementAt) { + final PyExpression problemElement = getProblemElement(elementAt); + if (problemElement != null) { + PsiReference reference = problemElement.getReference(); + final PsiElement resolved = reference != null ? reference.resolve() : null; + if (resolved instanceof PyTargetExpression) { + final PyExpression assignedValue = ((PyTargetExpression) resolved).findAssignedValue(); + if (assignedValue instanceof PyCallExpression) { + return (PyCallExpression) assignedValue; + } + } + } + + PyAssignmentStatement assignmentStatement = PsiTreeUtil.getParentOfType(elementAt, PyAssignmentStatement.class); + if (assignmentStatement != null) { + final PyExpression assignedValue = assignmentStatement.getAssignedValue(); + if (assignedValue instanceof PyCallExpression) { + return (PyCallExpression) assignedValue; + } + } + return PsiTreeUtil.getParentOfType(elementAt, PyCallExpression.class, false); + } + + @Nullable + static PyCallable getCallable(PsiElement elementAt) { + PyCallExpression callExpression = getCallExpression(elementAt); + + if (callExpression != null && elementAt != null) { + final PyCallable callable = callExpression.resolveCalleeFunction(getResolveContext(elementAt)); + return callable == null ? PsiTreeUtil.getParentOfType(elementAt, PyFunction.class) : callable; + } + return PsiTreeUtil.getParentOfType(elementAt, PyFunction.class); + } + + protected static PyResolveContext getResolveContext(@Nonnull PsiElement origin) { + return PyResolveContext.defaultContext() + .withTypeEvalContext(TypeEvalContext.codeAnalysis(origin.getProject(), origin.getContainingFile())); + } } \ No newline at end of file diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySingleQuotedDocstringInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySingleQuotedDocstringInspection.java index f8fc98de..5a63ff75 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySingleQuotedDocstringInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySingleQuotedDocstringInspection.java @@ -13,85 +13,92 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.inspections.quickfix.ConvertDocstringQuickFix; +import com.jetbrains.python.impl.psi.impl.PyStringLiteralExpressionImpl; import com.jetbrains.python.psi.PyDocStringOwner; import com.jetbrains.python.psi.PyStringLiteralExpression; -import com.jetbrains.python.impl.psi.impl.PyStringLiteralExpressionImpl; import consulo.annotation.component.ExtensionImpl; import consulo.document.util.TextRange; import consulo.language.editor.inspection.LocalInspectionToolSession; import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.util.PsiTreeUtil; -import org.jetbrains.annotations.Nls; - +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; /** - * User: catherine - *

* Inspection to detect docstrings not using triple double-quoted string + * + * @author catherine */ @ExtensionImpl public class PySingleQuotedDocstringInspection extends PyInspection { - - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.single.quoted.docstring"); - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } - - public static class Visitor extends PyInspectionVisitor { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameSingleQuotedDocstring(); } + @Nonnull @Override - public void visitPyStringLiteralExpression(final PyStringLiteralExpression string) { - String stringText = string.getText(); - int length = PyStringLiteralExpressionImpl.getPrefixLength(stringText); - stringText = stringText.substring(length); - final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(string, PyDocStringOwner.class); - if (docStringOwner != null) { - if (docStringOwner.getDocStringExpression() == string) { - if (!stringText.startsWith("\"\"\"") && !stringText.endsWith("\"\"\"")) { - ProblemsHolder holder = getHolder(); - if (holder != null) { - int quoteCount = 1; - if (stringText.startsWith("'''") && stringText.endsWith("'''")) { - quoteCount = 3; - } - TextRange trStart = new TextRange(length, length + quoteCount); - TextRange trEnd = new TextRange(stringText.length() + length - quoteCount, - stringText.length() + length); - if (string.getStringValue().isEmpty()) - holder.registerProblem(string, PyBundle.message("INSP.message.single.quoted.docstring"), - new ConvertDocstringQuickFix()); - else { - holder.registerProblem(string, trStart, - PyBundle.message("INSP.message.single.quoted.docstring"), new ConvertDocstringQuickFix()); - holder.registerProblem(string, trEnd, - PyBundle.message("INSP.message.single.quoted.docstring"), new ConvertDocstringQuickFix()); - } + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); + } + + public static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } + + @Override + public void visitPyStringLiteralExpression(final PyStringLiteralExpression string) { + String stringText = string.getText(); + int length = PyStringLiteralExpressionImpl.getPrefixLength(stringText); + stringText = stringText.substring(length); + final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(string, PyDocStringOwner.class); + if (docStringOwner != null) { + if (docStringOwner.getDocStringExpression() == string) { + if (!stringText.startsWith("\"\"\"") && !stringText.endsWith("\"\"\"")) { + ProblemsHolder holder = getHolder(); + if (holder != null) { + int quoteCount = 1; + if (stringText.startsWith("'''") && stringText.endsWith("'''")) { + quoteCount = 3; + } + TextRange trStart = new TextRange(length, length + quoteCount); + TextRange trEnd = new TextRange( + stringText.length() + length - quoteCount, + stringText.length() + length + ); + if (string.getStringValue().isEmpty()) { + holder.newProblem(PyLocalize.inspMessageSingleQuotedDocstring()) + .range(string) + .withFix(new ConvertDocstringQuickFix()) + .create(); + } + else { + holder.newProblem(PyLocalize.inspMessageSingleQuotedDocstring()) + .range(string, trStart) + .withFix(new ConvertDocstringQuickFix()) + .create(); + holder.newProblem(PyLocalize.inspMessageSingleQuotedDocstring()) + .range(string, trEnd) + .withFix(new ConvertDocstringQuickFix()) + .create(); + } + } + } + } } - } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyStatementEffectInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyStatementEffectInspection.java index 5b811c7a..a60810ee 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyStatementEffectInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyStatementEffectInspection.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.impl.inspections.quickfix.StatementEffectFunctionCallQuickFix; import com.jetbrains.python.impl.inspections.quickfix.StatementEffectIntroduceVariableQuickFix; @@ -27,8 +26,8 @@ import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.ResolveResult; import consulo.language.psi.util.PsiTreeUtil; -import org.jetbrains.annotations.Nls; - +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -37,137 +36,143 @@ */ @ExtensionImpl public class PyStatementEffectInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.statement.effect"); - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } - - @Override - protected boolean isSuppressForCodeFragment() { - return true; - } - - public static class Visitor extends PyInspectionVisitor { - - public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { - super(holder, session); + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameStatementEffect(); } + @Nonnull @Override - public void visitPyExpressionStatement(final PyExpressionStatement node) { - final PyExpression expression = node.getExpression(); - if (PsiTreeUtil.hasErrorElements(expression)) { - return; - } - if (hasEffect(expression)) { - return; - } - - // https://twitter.com/gvanrossum/status/112670605505077248 - if (expression instanceof PyStringLiteralExpression) { - return; - } - - final PyTryPart tryPart = PsiTreeUtil.getParentOfType(node, PyTryPart.class); - if (tryPart != null) { - final PyStatementList statementList = tryPart.getStatementList(); - if (statementList.getStatements().length == 1 && statementList.getStatements()[0] == node) { - return; - } - } - if (expression instanceof PyReferenceExpression && !((PyReferenceExpression)expression).isQualified()) { - registerProblem(expression, PyBundle.message("INSP.NAME.statement.message")); - } - else { - registerProblem(expression, PyBundle.message("INSP.NAME.statement.message"), new StatementEffectIntroduceVariableQuickFix()); - } + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } - private boolean hasEffect(@Nullable PyExpression expression) { - if (expression == null) { - return false; - } - if (expression instanceof PyCallExpression || expression instanceof PyYieldExpression) { + @Override + protected boolean isSuppressForCodeFragment() { return true; - } - else if (expression instanceof PyListCompExpression) { - if (hasEffect(((PyListCompExpression)expression).getResultExpression())) { - return true; + } + + public static class Visitor extends PyInspectionVisitor { + public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { + super(holder, session); } - } - else if (expression instanceof PyBinaryExpression) { - PyBinaryExpression binary = (PyBinaryExpression)expression; - final PyExpression leftExpression = binary.getLeftExpression(); - final PyExpression rightExpression = binary.getRightExpression(); - if (hasEffect(leftExpression) || hasEffect(rightExpression)) { - return true; + + @Override + public void visitPyExpressionStatement(final PyExpressionStatement node) { + final PyExpression expression = node.getExpression(); + if (PsiTreeUtil.hasErrorElements(expression)) { + return; + } + if (hasEffect(expression)) { + return; + } + + // https://twitter.com/gvanrossum/status/112670605505077248 + if (expression instanceof PyStringLiteralExpression) { + return; + } + + final PyTryPart tryPart = PsiTreeUtil.getParentOfType(node, PyTryPart.class); + if (tryPart != null) { + final PyStatementList statementList = tryPart.getStatementList(); + if (statementList.getStatements().length == 1 && statementList.getStatements()[0] == node) { + return; + } + } + if (expression instanceof PyReferenceExpression && !((PyReferenceExpression) expression).isQualified()) { + registerProblem(expression, PyLocalize.inspNameStatementMessage().get()); + } + else { + registerProblem( + expression, + PyLocalize.inspNameStatementMessage().get(), + new StatementEffectIntroduceVariableQuickFix() + ); + } } - final PyElementType operator = binary.getOperator(); - String method = operator == null ? null : operator.getSpecialMethodName(); - if (method != null) { - // maybe the op is overridden and may produce side effects, like cout << "hello" - PyType type = myTypeEvalContext.getType(leftExpression); - if (type != null && - !type.isBuiltin() && - type.resolveMember(method, null, AccessDirection.READ, getResolveContext()) != null) { - return true; - } - if (rightExpression != null) { - type = myTypeEvalContext.getType(rightExpression); - if (type != null) { - String rmethod = "__r" + method.substring(2); // __add__ -> __radd__ - if (!type.isBuiltin() && type.resolveMember(rmethod, null, AccessDirection.READ, getResolveContext()) != null) { + private boolean hasEffect(@Nullable PyExpression expression) { + if (expression == null) { + return false; + } + if (expression instanceof PyCallExpression || expression instanceof PyYieldExpression) { return true; - } } - } - } - } - else if (expression instanceof PyConditionalExpression) { - PyConditionalExpression conditionalExpression = (PyConditionalExpression)expression; - return hasEffect(conditionalExpression.getTruePart()) || hasEffect(conditionalExpression.getFalsePart()); - } - else if (expression instanceof PyParenthesizedExpression) { - PyParenthesizedExpression parenthesizedExpression = (PyParenthesizedExpression)expression; - return hasEffect(parenthesizedExpression.getContainedExpression()); - } - else if (expression instanceof PyReferenceExpression) { - PyReferenceExpression referenceExpression = (PyReferenceExpression)expression; - ResolveResult[] results = referenceExpression.getReference(getResolveContext()).multiResolve(true); - for (ResolveResult res : results) { - if (res.getElement() instanceof PyFunction) { - registerProblem(expression, + else if (expression instanceof PyListCompExpression) { + if (hasEffect(((PyListCompExpression) expression).getResultExpression())) { + return true; + } + } + else if (expression instanceof PyBinaryExpression) { + PyBinaryExpression binary = (PyBinaryExpression) expression; + final PyExpression leftExpression = binary.getLeftExpression(); + final PyExpression rightExpression = binary.getRightExpression(); + if (hasEffect(leftExpression) || hasEffect(rightExpression)) { + return true; + } + + final PyElementType operator = binary.getOperator(); + String method = operator == null ? null : operator.getSpecialMethodName(); + if (method != null) { + // maybe the op is overridden and may produce side effects, like cout << "hello" + PyType type = myTypeEvalContext.getType(leftExpression); + if (type != null && + !type.isBuiltin() && + type.resolveMember(method, null, AccessDirection.READ, getResolveContext()) != null) { + return true; + } + if (rightExpression != null) { + type = myTypeEvalContext.getType(rightExpression); + if (type != null) { + String rmethod = "__r" + method.substring(2); // __add__ -> __radd__ + if (!type.isBuiltin() && type.resolveMember(rmethod, null, AccessDirection.READ, getResolveContext()) != null) { + return true; + } + } + } + } + } + else if (expression instanceof PyConditionalExpression) { + PyConditionalExpression conditionalExpression = (PyConditionalExpression) expression; + return hasEffect(conditionalExpression.getTruePart()) || hasEffect(conditionalExpression.getFalsePart()); + } + else if (expression instanceof PyParenthesizedExpression) { + PyParenthesizedExpression parenthesizedExpression = (PyParenthesizedExpression) expression; + return hasEffect(parenthesizedExpression.getContainedExpression()); + } + else if (expression instanceof PyReferenceExpression) { + PyReferenceExpression referenceExpression = (PyReferenceExpression) expression; + ResolveResult[] results = referenceExpression.getReference(getResolveContext()).multiResolve(true); + for (ResolveResult res : results) { + if (res.getElement() instanceof PyFunction) { + registerProblem( + expression, "Statement seems to have no effect and can be replaced with function call to have effect", - new StatementEffectFunctionCallQuickFix()); - return true; - } - } - } - else if (expression instanceof PyTupleExpression) { - PyExpression[] elements = ((PyTupleExpression)expression).getElements(); - for (PyExpression element : elements) { - if (hasEffect(element)) { - return true; - } + new StatementEffectFunctionCallQuickFix() + ); + return true; + } + } + } + else if (expression instanceof PyTupleExpression) { + PyExpression[] elements = ((PyTupleExpression) expression).getElements(); + for (PyExpression element : elements) { + if (hasEffect(element)) { + return true; + } + } + } + else if (expression instanceof PyPrefixExpression) { + final PyPrefixExpression prefixExpr = (PyPrefixExpression) expression; + return prefixExpr.getOperator() == PyTokenTypes.AWAIT_KEYWORD; + } + return false; } - } - else if (expression instanceof PyPrefixExpression) { - final PyPrefixExpression prefixExpr = (PyPrefixExpression)expression; - return prefixExpr.getOperator() == PyTokenTypes.AWAIT_KEYWORD; - } - return false; } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyStringExceptionInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyStringExceptionInspection.java index 54411ee5..ed72ceda 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyStringExceptionInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyStringExceptionInspection.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.PyRaiseStatement; import com.jetbrains.python.psi.PyStringLiteralExpression; @@ -24,8 +22,8 @@ import consulo.language.editor.inspection.LocalInspectionToolSession; import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.psi.PsiElementVisitor; -import org.jetbrains.annotations.Nls; - +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -34,36 +32,37 @@ */ @ExtensionImpl public class PyStringExceptionInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.raising.string.exception"); - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } - - private static class Visitor extends PyInspectionVisitor { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameRaisingStringException(); } + @Nonnull @Override - public void visitPyRaiseStatement(PyRaiseStatement node) { - PyExpression[] expressions = node.getExpressions(); - if (expressions.length > 0) { - PyExpression expression = expressions[0]; - if (expression instanceof PyStringLiteralExpression) { - registerProblem(expression, "Raising a string exception"); + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); + } + + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } + + @Override + public void visitPyRaiseStatement(PyRaiseStatement node) { + PyExpression[] expressions = node.getExpressions(); + if (expressions.length > 0) { + PyExpression expression = expressions[0]; + if (expression instanceof PyStringLiteralExpression) { + registerProblem(expression, "Raising a string exception"); + } + } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyStringFormatInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyStringFormatInspection.java index 7efff21a..81f74784 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyStringFormatInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyStringFormatInspection.java @@ -13,32 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.inspections; import com.google.common.collect.ImmutableMap; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.impl.psi.PyUtil; +import com.jetbrains.python.impl.psi.impl.PyBuiltinCache; import com.jetbrains.python.impl.psi.types.PySubscriptableType; import com.jetbrains.python.impl.psi.types.PyTypeChecker; import com.jetbrains.python.impl.psi.types.PyTypeParser; import com.jetbrains.python.psi.*; -import com.jetbrains.python.impl.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.resolve.PyResolveContext; -import com.jetbrains.python.psi.types.*; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.TypeEvalContext; import consulo.annotation.component.ExtensionImpl; import consulo.language.editor.inspection.LocalInspectionToolSession; import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.psi.PsiElement; import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiFile; -import consulo.logging.Logger; -import org.jetbrains.annotations.Nls; - +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; + import java.math.BigInteger; import java.util.HashMap; import java.util.List; @@ -52,418 +51,435 @@ */ @ExtensionImpl public class PyStringFormatInspection extends PyInspection { - private static final Logger LOG = Logger.getInstance(PyStringFormatInspection.class); - - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.str.format"); - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull final ProblemsHolder holder, - final boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } - - public static class Visitor extends PyInspectionVisitor { - private static class Inspection { - private static final ImmutableMap FORMAT_CONVERSIONS = ImmutableMap.builder() - .put('d', "int or long or float") - .put('i', "int or long or float") - .put('o', "int or long or float") - .put('u', "int or long or float") - .put('x', "int or long or float") - .put('X', "int or long or float") - .put('e', "float") - .put('E', "float") - .put('f', "float") - .put('F', "float") - .put('g', "float") - .put('G', "float") - .put('c', "str") - .put('r', "str") - .put('s', "str") - .build(); - - private final Map myUsedMappingKeys = new HashMap(); - private int myExpectedArguments = 0; - private boolean myProblemRegister = false; - private final Visitor myVisitor; - private final TypeEvalContext myTypeEvalContext; - - private final Map myFormatSpec = new HashMap(); - - public Inspection(Visitor visitor, TypeEvalContext typeEvalContext) { - myVisitor = visitor; - myTypeEvalContext = typeEvalContext; - } - - // return number of arguments or -1 if it can not be computed - private int inspectArguments(@Nullable final PyExpression rightExpression, final PsiElement problemTarget) { - final Class[] SIMPLE_RHS_EXPRESSIONS = { - PyLiteralExpression.class, PySubscriptionExpression.class, PyBinaryExpression.class, PyConditionalExpression.class - }; - final Class[] LIST_LIKE_EXPRESSIONS = {PyListLiteralExpression.class, PyListCompExpression.class}; - final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(problemTarget); - final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(myTypeEvalContext); - - //in case tuple multiplies integer PY-4647 - if (rightExpression instanceof PyBinaryExpression) { - PyBinaryExpression binaryExpression = (PyBinaryExpression)rightExpression; - if (binaryExpression.getOperator() == PyTokenTypes.MULT && binaryExpression.getLeftExpression() instanceof PyParenthesizedExpression - && binaryExpression.getRightExpression() instanceof PyNumericLiteralExpression) { - PyParenthesizedExpression parenthesizedExpression = (PyParenthesizedExpression)binaryExpression.getLeftExpression(); - if (parenthesizedExpression.getContainedExpression() instanceof PyTupleExpression) { - PyExpression[] tupleElements = ((PyTupleExpression)parenthesizedExpression.getContainedExpression()).getElements(); - return ((PyNumericLiteralExpression)((PyBinaryExpression)rightExpression).getRightExpression()).getBigIntegerValue() - .intValue() * tupleElements.length; + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameStrFormat(); + } + + @Nonnull + @Override + public PsiElementVisitor buildVisitor( + @Nonnull final ProblemsHolder holder, + final boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); + } + + public static class Visitor extends PyInspectionVisitor { + private static class Inspection { + private static final ImmutableMap FORMAT_CONVERSIONS = ImmutableMap.builder() + .put('d', "int or long or float") + .put('i', "int or long or float") + .put('o', "int or long or float") + .put('u', "int or long or float") + .put('x', "int or long or float") + .put('X', "int or long or float") + .put('e', "float") + .put('E', "float") + .put('f', "float") + .put('F', "float") + .put('g', "float") + .put('G', "float") + .put('c', "str") + .put('r', "str") + .put('s', "str") + .build(); + + private final Map myUsedMappingKeys = new HashMap(); + private int myExpectedArguments = 0; + private boolean myProblemRegister = false; + private final Visitor myVisitor; + private final TypeEvalContext myTypeEvalContext; + + private final Map myFormatSpec = new HashMap(); + + public Inspection(Visitor visitor, TypeEvalContext typeEvalContext) { + myVisitor = visitor; + myTypeEvalContext = typeEvalContext; } - } - } - final String s = myFormatSpec.get("1"); - if (PyUtil.instanceOf(rightExpression, SIMPLE_RHS_EXPRESSIONS)) { - if (s != null) { - assert rightExpression != null; - PyType right_type = myTypeEvalContext.getType(rightExpression); - if (right_type instanceof PySubscriptableType) { - PySubscriptableType tuple_type = (PySubscriptableType)right_type; - for (int i = 0; i <= tuple_type.getElementCount(); i += 1) { - PyType elementType = tuple_type.getElementType(i); - if (elementType != null) { - final String typeName = myFormatSpec.get(String.valueOf(i + 1)); - final PyType type = typeName != null ? PyTypeParser.getTypeByName(problemTarget, typeName) : null; - checkTypeCompatible(problemTarget, elementType, type); + + // return number of arguments or -1 if it can not be computed + private int inspectArguments(@Nullable final PyExpression rightExpression, final PsiElement problemTarget) { + final Class[] SIMPLE_RHS_EXPRESSIONS = { + PyLiteralExpression.class, PySubscriptionExpression.class, PyBinaryExpression.class, PyConditionalExpression.class + }; + final Class[] LIST_LIKE_EXPRESSIONS = {PyListLiteralExpression.class, PyListCompExpression.class}; + final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(problemTarget); + final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(myTypeEvalContext); + + //in case tuple multiplies integer PY-4647 + if (rightExpression instanceof PyBinaryExpression) { + PyBinaryExpression binaryExpression = (PyBinaryExpression) rightExpression; + if (binaryExpression.getOperator() == PyTokenTypes.MULT && binaryExpression.getLeftExpression() instanceof PyParenthesizedExpression + && binaryExpression.getRightExpression() instanceof PyNumericLiteralExpression) { + PyParenthesizedExpression parenthesizedExpression = + (PyParenthesizedExpression) binaryExpression.getLeftExpression(); + if (parenthesizedExpression.getContainedExpression() instanceof PyTupleExpression) { + PyExpression[] tupleElements = + ((PyTupleExpression) parenthesizedExpression.getContainedExpression()).getElements(); + return ((PyNumericLiteralExpression) ((PyBinaryExpression) rightExpression).getRightExpression()).getBigIntegerValue() + .intValue() * tupleElements.length; + } + } + } + final String s = myFormatSpec.get("1"); + if (PyUtil.instanceOf(rightExpression, SIMPLE_RHS_EXPRESSIONS)) { + if (s != null) { + assert rightExpression != null; + PyType right_type = myTypeEvalContext.getType(rightExpression); + if (right_type instanceof PySubscriptableType) { + PySubscriptableType tuple_type = (PySubscriptableType) right_type; + for (int i = 0; i <= tuple_type.getElementCount(); i += 1) { + PyType elementType = tuple_type.getElementType(i); + if (elementType != null) { + final String typeName = myFormatSpec.get(String.valueOf(i + 1)); + final PyType type = typeName != null ? PyTypeParser.getTypeByName(problemTarget, typeName) : null; + checkTypeCompatible(problemTarget, elementType, type); + } + } + return tuple_type.getElementCount(); + } + else { + checkExpressionType(rightExpression, s, problemTarget); + } + } + return 1; + } + else if (rightExpression instanceof PyReferenceExpression) { + if (PyNames.DICT.equals(rightExpression.getName())) { + return -1; + } + + final PsiElement pyElement = + ((PyReferenceExpression) rightExpression).followAssignmentsChain(resolveContext).getElement(); + if (pyElement == rightExpression || !(pyElement instanceof PyExpression)) { + return -1; + } + if (pyElement instanceof PyDictLiteralExpression) { + return inspectDict(rightExpression, problemTarget, true); + } + return inspectArguments((PyExpression) pyElement, problemTarget); + } + else if (rightExpression instanceof PyCallExpression) { + final PyCallable callable = ((PyCallExpression) rightExpression).resolveCalleeFunction(resolveContext); + // TODO: Switch to Callable.getReturnType() + if (callable instanceof PyFunction && myTypeEvalContext.maySwitchToAST((PyFunction) callable)) { + PyStatementList statementList = ((PyFunction) callable).getStatementList(); + if (statementList == null) { + return -1; + } + PyReturnStatement[] returnStatements = PyUtil.getAllChildrenOfType(statementList, PyReturnStatement.class); + int expressionsSize = -1; + for (PyReturnStatement returnStatement : returnStatements) { + if (returnStatement.getExpression() instanceof PyCallExpression) { + return -1; + } + List expressionList = PyUtil.flattenedParensAndTuples(returnStatement.getExpression()); + if (expressionsSize < 0) { + expressionsSize = expressionList.size(); + } + if (expressionsSize != expressionList.size()) { + return -1; + } + } + return expressionsSize; + } + return -1; + } + else if (rightExpression instanceof PyParenthesizedExpression) { + final PyExpression rhs = ((PyParenthesizedExpression) rightExpression).getContainedExpression(); + return inspectArguments(rhs, rhs); + } + else if (rightExpression instanceof PyTupleExpression) { + final PyExpression[] expressions = ((PyTupleExpression) rightExpression).getElements(); + int i = 1; + for (PyExpression expression : expressions) { + final String formatSpec = myFormatSpec.get(Integer.toString(i)); + if (formatSpec != null) { + checkExpressionType(expression, formatSpec, expression); + } + ++i; + } + return expressions.length; + } + else if (rightExpression instanceof PyDictLiteralExpression) { + return inspectDict(rightExpression, problemTarget, false); + } + else if (PyUtil.instanceOf(rightExpression, LIST_LIKE_EXPRESSIONS)) { + if (s != null) { + checkTypeCompatible(problemTarget, builtinCache.getStrType(), + PyTypeParser.getTypeByName(problemTarget, s) + ); + return 1; + } + } + else if (rightExpression instanceof PySliceExpression && s != null) { + final PyType type = myTypeEvalContext.getType(((PySliceExpression) rightExpression).getOperand()); + final PyType stringType = + PyBuiltinCache.getInstance(rightExpression).getStringType(LanguageLevel.forElement(rightExpression)); + final PyType listType = PyBuiltinCache.getInstance(rightExpression).getListType(); + + if (type == null || PyTypeChecker.match(listType, type, myTypeEvalContext) + || PyTypeChecker.match(stringType, type, myTypeEvalContext)) { + checkTypeCompatible(problemTarget, builtinCache.getStrType(), + PyTypeParser.getTypeByName(problemTarget, s) + ); + return 1; + } + PySliceItem sliceItem = ((PySliceExpression) rightExpression).getSliceItem(); + if (sliceItem != null) { + PyExpression lower = sliceItem.getLowerBound(); + PyExpression upper = sliceItem.getUpperBound(); + PyExpression stride = sliceItem.getStride(); + if (upper instanceof PyNumericLiteralExpression) { + BigInteger lowerVal; + if (lower instanceof PyNumericLiteralExpression) { + lowerVal = ((PyNumericLiteralExpression) lower).getBigIntegerValue(); + } + else { + lowerVal = BigInteger.ZERO; + } + int count = (((PyNumericLiteralExpression) upper).getBigIntegerValue().subtract(lowerVal)).intValue(); + int strideVal; + if (stride instanceof PyNumericLiteralExpression) { + strideVal = ((PyNumericLiteralExpression) stride).getBigIntegerValue().intValue(); + } + else { + strideVal = 1; + } + int res = count / strideVal; + int residue = count % strideVal == 0 ? 0 : 1; + return res + residue; + } + } + return -1; } - } - return tuple_type.getElementCount(); - } - else checkExpressionType(rightExpression, s, problemTarget); - } - return 1; - } - else if (rightExpression instanceof PyReferenceExpression) { - if (PyNames.DICT.equals(rightExpression.getName())) return -1; - - final PsiElement pyElement = ((PyReferenceExpression)rightExpression).followAssignmentsChain(resolveContext).getElement(); - if (pyElement == rightExpression || !(pyElement instanceof PyExpression)) { - return -1; - } - if (pyElement instanceof PyDictLiteralExpression) { - return inspectDict(rightExpression, problemTarget, true); - } - return inspectArguments((PyExpression)pyElement, problemTarget); - } - else if (rightExpression instanceof PyCallExpression) { - final PyCallable callable = ((PyCallExpression)rightExpression).resolveCalleeFunction(resolveContext); - // TODO: Switch to Callable.getReturnType() - if (callable instanceof PyFunction && myTypeEvalContext.maySwitchToAST((PyFunction)callable)) { - PyStatementList statementList = ((PyFunction)callable).getStatementList(); - if (statementList == null) { - return -1; - } - PyReturnStatement[] returnStatements = PyUtil.getAllChildrenOfType(statementList, PyReturnStatement.class); - int expressionsSize = -1; - for (PyReturnStatement returnStatement : returnStatements) { - if (returnStatement.getExpression() instanceof PyCallExpression) { - return -1; - } - List expressionList = PyUtil.flattenedParensAndTuples(returnStatement.getExpression()); - if (expressionsSize < 0) { - expressionsSize = expressionList.size(); - } - if (expressionsSize != expressionList.size()) { return -1; - } } - return expressionsSize; - } - return -1; - } - else if (rightExpression instanceof PyParenthesizedExpression) { - final PyExpression rhs = ((PyParenthesizedExpression)rightExpression).getContainedExpression(); - return inspectArguments(rhs, rhs); - } - else if (rightExpression instanceof PyTupleExpression) { - final PyExpression[] expressions = ((PyTupleExpression)rightExpression).getElements(); - int i = 1; - for (PyExpression expression : expressions) { - final String formatSpec = myFormatSpec.get(Integer.toString(i)); - if (formatSpec != null) { - checkExpressionType(expression, formatSpec, expression); - } - ++i; - } - return expressions.length; - } - else if (rightExpression instanceof PyDictLiteralExpression) { - return inspectDict(rightExpression, problemTarget, false); - } - else if (PyUtil.instanceOf(rightExpression, LIST_LIKE_EXPRESSIONS)) { - if (s != null) { - checkTypeCompatible(problemTarget, builtinCache.getStrType(), - PyTypeParser.getTypeByName(problemTarget, s)); - return 1; - } - } - else if (rightExpression instanceof PySliceExpression && s != null) { - final PyType type = myTypeEvalContext.getType(((PySliceExpression)rightExpression).getOperand()); - final PyType stringType = PyBuiltinCache.getInstance(rightExpression).getStringType(LanguageLevel.forElement(rightExpression)); - final PyType listType = PyBuiltinCache.getInstance(rightExpression).getListType(); - - if (type == null || PyTypeChecker.match(listType, type, myTypeEvalContext) - || PyTypeChecker.match(stringType, type, myTypeEvalContext)) { - checkTypeCompatible(problemTarget, builtinCache.getStrType(), - PyTypeParser.getTypeByName(problemTarget, s)); - return 1; - } - PySliceItem sliceItem = ((PySliceExpression)rightExpression).getSliceItem(); - if (sliceItem != null) { - PyExpression lower = sliceItem.getLowerBound(); - PyExpression upper = sliceItem.getUpperBound(); - PyExpression stride = sliceItem.getStride(); - if (upper instanceof PyNumericLiteralExpression) { - BigInteger lowerVal; - if (lower instanceof PyNumericLiteralExpression) { - lowerVal = ((PyNumericLiteralExpression)lower).getBigIntegerValue(); - } - else { - lowerVal = BigInteger.ZERO; - } - int count = (((PyNumericLiteralExpression)upper).getBigIntegerValue().subtract(lowerVal)).intValue(); - int strideVal; - if (stride instanceof PyNumericLiteralExpression) - strideVal = ((PyNumericLiteralExpression)stride).getBigIntegerValue().intValue(); - else - strideVal = 1; - int res = count / strideVal; - int residue = count % strideVal == 0 ? 0 : 1; - return res + residue; + + + private static Map addSubscriptions(PsiFile file, String operand) { + Map additionalExpressions = new HashMap(); + PySubscriptionExpression[] subscriptionExpressions = PyUtil.getAllChildrenOfType(file, PySubscriptionExpression.class); + for (PySubscriptionExpression expr : subscriptionExpressions) { + if (expr.getOperand().getText().equals(operand)) { + PsiElement parent = expr.getParent(); + if (parent instanceof PyAssignmentStatement) { + if (expr.equals(((PyAssignmentStatement) parent).getLeftHandSideExpression())) { + PyExpression key = expr.getIndexExpression(); + if (key != null) { + additionalExpressions.put(key, ((PyAssignmentStatement) parent).getAssignedValue()); + } + } + } + } + } + return additionalExpressions; } - } - return -1; - } - return -1; - } - - - private static Map addSubscriptions(PsiFile file, String operand) { - Map additionalExpressions = new HashMap(); - PySubscriptionExpression[] subscriptionExpressions = PyUtil.getAllChildrenOfType(file, PySubscriptionExpression.class); - for (PySubscriptionExpression expr : subscriptionExpressions) { - if (expr.getOperand().getText().equals(operand)) { - PsiElement parent = expr.getParent(); - if (parent instanceof PyAssignmentStatement) { - if (expr.equals(((PyAssignmentStatement)parent).getLeftHandSideExpression())) { - PyExpression key = expr.getIndexExpression(); - if (key != null) { - additionalExpressions.put(key, ((PyAssignmentStatement)parent).getAssignedValue()); + + // inspects dict expressions. Finds key-value pairs from subscriptions if addSubscriptions is true. + private int inspectDict(PyExpression rightExpression, PsiElement problemTarget, boolean addSubscriptions) { + PsiElement pyElement; + Map additionalExpressions; + if (addSubscriptions) { + additionalExpressions = addSubscriptions( + rightExpression.getContainingFile(), + rightExpression.getText() + ); + pyElement = ((PyReferenceExpression) rightExpression).followAssignmentsChain(PyResolveContext.noImplicits() + .withTypeEvalContext(myTypeEvalContext)) + .getElement(); + } + else { + additionalExpressions = new HashMap(); + pyElement = rightExpression; + } + + final PyKeyValueExpression[] expressions = ((PyDictLiteralExpression) pyElement).getElements(); + if (myUsedMappingKeys.isEmpty()) { + if (myExpectedArguments > 0) { + if (myExpectedArguments == (expressions.length + additionalExpressions.size())) { + // probably "%s %s" % {'a':1, 'b':2}, with names forgotten in template + registerProblem(rightExpression, PyLocalize.inspFormatRequiresNoMapping().get()); + } + else { + // "braces: %s" % {'foo':1} gives "braces: {'foo':1}", implicit str() kicks in + return 1; + } + } + else { + // "foo" % {whatever} is just "foo" + return 0; + } } - } + for (PyKeyValueExpression expression : expressions) { + final PyExpression key = expression.getKey(); + if (key instanceof PyStringLiteralExpression) { + final String name = ((PyStringLiteralExpression) key).getStringValue(); + if (myUsedMappingKeys.get(name) != null) { + myUsedMappingKeys.put(name, true); + final PyExpression value = expression.getValue(); + if (value != null) { + checkExpressionType(value, myFormatSpec.get(name), problemTarget); + } + } + } + } + for (Map.Entry expression : additionalExpressions.entrySet()) { + final PyExpression key = expression.getKey(); + if (key instanceof PyStringLiteralExpression) { + final String name = ((PyStringLiteralExpression) key).getStringValue(); + if (myUsedMappingKeys.get(name) != null) { + myUsedMappingKeys.put(name, true); + final PyExpression value = expression.getValue(); + if (value != null) { + checkExpressionType(value, myFormatSpec.get(name), problemTarget); + } + } + } + } + for (String key : myUsedMappingKeys.keySet()) { + if (!myUsedMappingKeys.get(key).booleanValue()) { + registerProblem(problemTarget, PyLocalize.inspKey$0HasNoArg(key).get()); + break; + } + } + return (expressions.length + additionalExpressions.size()); } - } - } - return additionalExpressions; - } - - // inspects dict expressions. Finds key-value pairs from subscriptions if addSubscriptions is true. - private int inspectDict(PyExpression rightExpression, PsiElement problemTarget, boolean addSubscriptions) { - PsiElement pyElement; - Map additionalExpressions; - if (addSubscriptions) { - additionalExpressions = addSubscriptions(rightExpression.getContainingFile(), - rightExpression.getText()); - pyElement = ((PyReferenceExpression)rightExpression).followAssignmentsChain(PyResolveContext.noImplicits() - .withTypeEvalContext(myTypeEvalContext)) - .getElement(); - } - else { - additionalExpressions = new HashMap(); - pyElement = rightExpression; - } - final PyKeyValueExpression[] expressions = ((PyDictLiteralExpression)pyElement).getElements(); - if (myUsedMappingKeys.isEmpty()) { - if (myExpectedArguments > 0) { - if (myExpectedArguments == (expressions.length + additionalExpressions.size())) { - // probably "%s %s" % {'a':1, 'b':2}, with names forgotten in template - registerProblem(rightExpression, PyBundle.message("INSP.format.requires.no.mapping")); + private void registerProblem(@Nonnull PsiElement problemTarget, @Nonnull final String message) { + myProblemRegister = true; + myVisitor.registerProblem(problemTarget, message); } - else { - // "braces: %s" % {'foo':1} gives "braces: {'foo':1}", implicit str() kicks in - return 1; + + private void checkExpressionType( + @Nonnull final PyExpression expression, + @Nonnull final String expectedTypeName, + PsiElement problemTarget + ) { + final PyType actual = myTypeEvalContext.getType(expression); + final PyType expected = PyTypeParser.getTypeByName(problemTarget, expectedTypeName); + if (actual != null) { + checkTypeCompatible(problemTarget, actual, expected); + } } - } - else { - // "foo" % {whatever} is just "foo" - return 0; - } - } - for (PyKeyValueExpression expression : expressions) { - final PyExpression key = expression.getKey(); - if (key instanceof PyStringLiteralExpression) { - final String name = ((PyStringLiteralExpression)key).getStringValue(); - if (myUsedMappingKeys.get(name) != null) { - myUsedMappingKeys.put(name, true); - final PyExpression value = expression.getValue(); - if (value != null) { - checkExpressionType(value, myFormatSpec.get(name), problemTarget); - } + + private void checkTypeCompatible( + @Nonnull final PsiElement problemTarget, + @Nullable final PyType actual, + @Nullable final PyType expected + ) { + if (expected != null && "str".equals(expected.getName())) { + return; + } + if (actual != null && !PyTypeChecker.match(expected, actual, myTypeEvalContext)) { + registerProblem(problemTarget, PyLocalize.inspUnexpectedType$0(actual.getName()).get()); + } } - } - } - for (Map.Entry expression : additionalExpressions.entrySet()) { - final PyExpression key = expression.getKey(); - if (key instanceof PyStringLiteralExpression) { - final String name = ((PyStringLiteralExpression)key).getStringValue(); - if (myUsedMappingKeys.get(name) != null) { - myUsedMappingKeys.put(name, true); - final PyExpression value = expression.getValue(); - if (value != null) { - checkExpressionType(value, myFormatSpec.get(name), problemTarget); - } + + private void inspectFormat(@Nonnull final PyStringLiteralExpression formatExpression) { + final String value = formatExpression.getStringValue(); + final List chunks = filterSubstitutions(parsePercentFormat(value)); + + // 1. The '%' character + // Skip the first item in the sections, it's always empty + myExpectedArguments = chunks.size(); + myUsedMappingKeys.clear(); + + // if use mapping keys + final boolean mapping = chunks.size() > 0 && chunks.get(0).getMappingKey() != null; + for (int i = 0; i < chunks.size(); ++i) { + PyStringFormatParser.SubstitutionChunk chunk = chunks.get(i); + + // 2. Mapping key + String mappingKey = Integer.toString(i + 1); + if (mapping) { + if (chunk.getMappingKey() == null || chunk.isUnclosedMapping()) { + registerProblem(formatExpression, PyLocalize.inspTooFewKeys().get()); + break; + } + mappingKey = chunk.getMappingKey(); + myUsedMappingKeys.put(mappingKey, false); + } + + // 4. Minimum field width + inspectWidth(formatExpression, chunk.getWidth()); + + // 5. Precision + inspectWidth(formatExpression, chunk.getPrecision()); + + // 7. Format specifier + if (FORMAT_CONVERSIONS.containsKey(chunk.getConversionType())) { + myFormatSpec.put(mappingKey, FORMAT_CONVERSIONS.get(chunk.getConversionType())); + continue; + } + registerProblem(formatExpression, PyLocalize.inspNoFormatSpecifierChar().get()); + } } - } - } - for (String key : myUsedMappingKeys.keySet()) { - if (!myUsedMappingKeys.get(key).booleanValue()) { - registerProblem(problemTarget, PyBundle.message("INSP.key.$0.has.no.arg", key)); - break; - } - } - return (expressions.length + additionalExpressions.size()); - } - - private void registerProblem(@Nonnull PsiElement problemTarget, @Nonnull final String message) { - myProblemRegister = true; - myVisitor.registerProblem(problemTarget, message); - } - - private void checkExpressionType(@Nonnull final PyExpression expression, - @Nonnull final String expectedTypeName, - PsiElement problemTarget) { - final PyType actual = myTypeEvalContext.getType(expression); - final PyType expected = PyTypeParser.getTypeByName(problemTarget, expectedTypeName); - if (actual != null) { - checkTypeCompatible(problemTarget, actual, expected); - } - } - private void checkTypeCompatible(@Nonnull final PsiElement problemTarget, - @Nullable final PyType actual, - @Nullable final PyType expected) { - if (expected != null && "str".equals(expected.getName())) { - return; - } - if (actual != null && !PyTypeChecker.match(expected, actual, myTypeEvalContext)) { - registerProblem(problemTarget, PyBundle.message("INSP.unexpected.type.$0", actual.getName())); - } - } - - private void inspectFormat(@Nonnull final PyStringLiteralExpression formatExpression) { - final String value = formatExpression.getStringValue(); - final List chunks = filterSubstitutions(parsePercentFormat(value)); - - // 1. The '%' character - // Skip the first item in the sections, it's always empty - myExpectedArguments = chunks.size(); - myUsedMappingKeys.clear(); - - // if use mapping keys - final boolean mapping = chunks.size() > 0 && chunks.get(0).getMappingKey() != null; - for (int i = 0; i < chunks.size(); ++i) { - PyStringFormatParser.SubstitutionChunk chunk = chunks.get(i); - - // 2. Mapping key - String mappingKey = Integer.toString(i + 1); - if (mapping) { - if (chunk.getMappingKey() == null || chunk.isUnclosedMapping()) { - registerProblem(formatExpression, PyBundle.message("INSP.too.few.keys")); - break; + private void inspectWidth(@Nonnull final PyStringLiteralExpression formatExpression, String width) { + if ("*".equals(width)) { + ++myExpectedArguments; + if (myUsedMappingKeys.size() > 0) { + registerProblem(formatExpression, "Can't use \'*\' in formats when using a mapping"); + } + } } - mappingKey = chunk.getMappingKey(); - myUsedMappingKeys.put(mappingKey, false); - } - - // 4. Minimum field width - inspectWidth(formatExpression, chunk.getWidth()); - - // 5. Precision - inspectWidth(formatExpression, chunk.getPrecision()); - - // 7. Format specifier - if (FORMAT_CONVERSIONS.containsKey(chunk.getConversionType())) { - myFormatSpec.put(mappingKey, FORMAT_CONVERSIONS.get(chunk.getConversionType())); - continue; - } - registerProblem(formatExpression, PyBundle.message("INSP.no.format.specifier.char")); - } - } - - private void inspectWidth(@Nonnull final PyStringLiteralExpression formatExpression, String width) { - if ("*".equals(width)) { - ++myExpectedArguments; - if (myUsedMappingKeys.size() > 0) { - registerProblem(formatExpression, "Can't use \'*\' in formats when using a mapping"); - } - } - } - public boolean isProblem() { - return myProblemRegister; - } + public boolean isProblem() { + return myProblemRegister; + } - private void inspectValues(@Nullable final PyExpression rightExpression) { - if (rightExpression == null) { - return; - } - if (rightExpression instanceof PyParenthesizedExpression) { - inspectValues(((PyParenthesizedExpression)rightExpression).getContainedExpression()); - } - else { - final PyType type = myTypeEvalContext.getType(rightExpression); - if (type != null) { - if (myUsedMappingKeys.size() > 0 && !("dict".equals(type.getName()))) { - registerProblem(rightExpression, PyBundle.message("INSP.format.requires.mapping")); - return; + private void inspectValues(@Nullable final PyExpression rightExpression) { + if (rightExpression == null) { + return; + } + if (rightExpression instanceof PyParenthesizedExpression) { + inspectValues(((PyParenthesizedExpression) rightExpression).getContainedExpression()); + } + else { + final PyType type = myTypeEvalContext.getType(rightExpression); + if (type != null) { + if (myUsedMappingKeys.size() > 0 && !("dict".equals(type.getName()))) { + registerProblem(rightExpression, PyLocalize.inspFormatRequiresMapping().get()); + return; + } + } + inspectArgumentsNumber(rightExpression); + } + } + + private void inspectArgumentsNumber(@Nonnull final PyExpression rightExpression) { + final int arguments = inspectArguments(rightExpression, rightExpression); + if (myUsedMappingKeys.isEmpty() && arguments >= 0) { + if (myExpectedArguments < arguments) { + registerProblem(rightExpression, PyLocalize.inspTooManyArgsForFmtString().get()); + } + else if (myExpectedArguments > arguments) { + registerProblem(rightExpression, PyLocalize.inspTooFewArgsForFmtString().get()); + } + } } - } - inspectArgumentsNumber(rightExpression); - } - } - - private void inspectArgumentsNumber(@Nonnull final PyExpression rightExpression) { - final int arguments = inspectArguments(rightExpression, rightExpression); - if (myUsedMappingKeys.isEmpty() && arguments >= 0) { - if (myExpectedArguments < arguments) { - registerProblem(rightExpression, PyBundle.message("INSP.too.many.args.for.fmt.string")); - } - else if (myExpectedArguments > arguments) { - registerProblem(rightExpression, PyBundle.message("INSP.too.few.args.for.fmt.string")); - } } - } - } - public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { - super(holder, session); - } + public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { + super(holder, session); + } - @Override - public void visitPyBinaryExpression(final PyBinaryExpression node) { - if (node.getLeftExpression() instanceof PyStringLiteralExpression && node.isOperator("%")) { - final Inspection inspection = new Inspection(this, myTypeEvalContext); - final PyStringLiteralExpression literalExpression = (PyStringLiteralExpression)node.getLeftExpression(); - inspection.inspectFormat(literalExpression); - if (inspection.isProblem()) { - return; + @Override + public void visitPyBinaryExpression(final PyBinaryExpression node) { + if (node.getLeftExpression() instanceof PyStringLiteralExpression && node.isOperator("%")) { + final Inspection inspection = new Inspection(this, myTypeEvalContext); + final PyStringLiteralExpression literalExpression = (PyStringLiteralExpression) node.getLeftExpression(); + inspection.inspectFormat(literalExpression); + if (inspection.isProblem()) { + return; + } + inspection.inspectValues(node.getRightExpression()); + } } - inspection.inspectValues(node.getRightExpression()); - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySuperArgumentsInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySuperArgumentsInspection.java index 4b9f7c60..c9818a80 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySuperArgumentsInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySuperArgumentsInspection.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.PyCallExpression; import com.jetbrains.python.psi.PyClass; @@ -28,8 +27,8 @@ import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.psi.PsiElement; import consulo.language.psi.PsiElementVisitor; -import org.jetbrains.annotations.Nls; - +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -38,64 +37,67 @@ */ @ExtensionImpl public class PySuperArgumentsInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.wrong.super.arguments"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameWrongSuperArguments(); + } - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } + @Nonnull + @Override + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); + } - private static class Visitor extends PyInspectionVisitor { + private static class Visitor extends PyInspectionVisitor { - public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { - super(holder, session); - } + public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { + super(holder, session); + } - @Override - public void visitPyCallExpression(PyCallExpression node) { - final PyExpression callee = node.getCallee(); - if (callee != null) { - if (PyNames.SUPER.equals(callee.getName())) { - PyExpression[] arguments = node.getArguments(); - if (arguments.length == 2) { - if (arguments[0] instanceof PyReferenceExpression && arguments[1] instanceof PyReferenceExpression) { - PyClass firstClass = findClassOf(arguments[0]); - PyClass secondClass = findClassOf(arguments[1]); - if (firstClass != null && secondClass != null) { - if (!secondClass.isSubclass(firstClass, myTypeEvalContext)) { - registerProblem(node.getArgumentList(), - PyBundle.message("INSP.$0.is.not.superclass.of.$1", secondClass.getName(), firstClass.getName())); + @Override + public void visitPyCallExpression(PyCallExpression node) { + final PyExpression callee = node.getCallee(); + if (callee != null) { + if (PyNames.SUPER.equals(callee.getName())) { + PyExpression[] arguments = node.getArguments(); + if (arguments.length == 2) { + if (arguments[0] instanceof PyReferenceExpression && arguments[1] instanceof PyReferenceExpression) { + PyClass firstClass = findClassOf(arguments[0]); + PyClass secondClass = findClassOf(arguments[1]); + if (firstClass != null && secondClass != null) { + if (!secondClass.isSubclass(firstClass, myTypeEvalContext)) { + registerProblem( + node.getArgumentList(), + PyLocalize.insp$0IsNotSuperclassOf$1(secondClass.getName(), firstClass.getName()).get() + ); + } + } + } + } } - } } - } } - } - } - @Nullable - private PyClass findClassOf(PyExpression argument) { - PsiElement firstElement = ((PyReferenceExpression)argument).followAssignmentsChain(getResolveContext()).getElement(); - PyClass firstClass = null; - if (firstElement instanceof PyClass) { - firstClass = (PyClass)firstElement; - } - else if (firstElement instanceof PyExpression) { - PyType first_type = myTypeEvalContext.getType((PyExpression)firstElement); - if (first_type instanceof PyClassType) { - firstClass = ((PyClassType)first_type).getPyClass(); + @Nullable + private PyClass findClassOf(PyExpression argument) { + PsiElement firstElement = ((PyReferenceExpression) argument).followAssignmentsChain(getResolveContext()).getElement(); + PyClass firstClass = null; + if (firstElement instanceof PyClass) { + firstClass = (PyClass) firstElement; + } + else if (firstElement instanceof PyExpression) { + PyType first_type = myTypeEvalContext.getType((PyExpression) firstElement); + if (first_type instanceof PyClassType) { + firstClass = ((PyClassType) first_type).getPyClass(); + } + } + return firstClass; } - } - return firstClass; } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTrailingSemicolonInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTrailingSemicolonInspection.java index ceaac006..e650446e 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTrailingSemicolonInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTrailingSemicolonInspection.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.impl.inspections.quickfix.RemoveTrailingSemicolonQuickFix; import com.jetbrains.python.psi.PyStatement; @@ -27,8 +25,8 @@ import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiWhiteSpace; -import org.jetbrains.annotations.Nls; - +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -37,41 +35,43 @@ */ @ExtensionImpl public class PyTrailingSemicolonInspection extends PyInspection { - @Nls - @Nonnull - public String getDisplayName() { - return PyBundle.message("INSP.NAME.trailing.semicolon"); - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } - - public static class Visitor extends PyInspectionVisitor { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameTrailingSemicolon(); } + @Nonnull @Override - public void visitPyStatement(final PyStatement statement) { - ASTNode statementNode = statement.getNode(); - if (statementNode != null) { - ASTNode[] nodes = statementNode.getChildren(TokenSet.create(PyTokenTypes.SEMICOLON)); - for (ASTNode node : nodes) { - ASTNode nextNode = statementNode.getTreeNext(); - while ((nextNode instanceof PsiWhiteSpace) && (!nextNode.textContains('\n'))) { - nextNode = nextNode.getTreeNext(); - } - if (nextNode == null || nextNode.textContains('\n')) { - registerProblem(node.getPsi(), "Trailing semicolon in the statement", new RemoveTrailingSemicolonQuickFix()); - } + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); + } + + public static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } + + @Override + public void visitPyStatement(final PyStatement statement) { + ASTNode statementNode = statement.getNode(); + if (statementNode != null) { + ASTNode[] nodes = statementNode.getChildren(TokenSet.create(PyTokenTypes.SEMICOLON)); + for (ASTNode node : nodes) { + ASTNode nextNode = statementNode.getTreeNext(); + while ((nextNode instanceof PsiWhiteSpace) && (!nextNode.textContains('\n'))) { + nextNode = nextNode.getTreeNext(); + } + if (nextNode == null || nextNode.textContains('\n')) { + registerProblem(node.getPsi(), "Trailing semicolon in the statement", new RemoveTrailingSemicolonQuickFix()); + } + } + } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTupleAssignmentBalanceInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTupleAssignmentBalanceInspection.java index 41fccf56..12a6bdb3 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTupleAssignmentBalanceInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTupleAssignmentBalanceInspection.java @@ -15,17 +15,16 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.psi.PyUtil; -import com.jetbrains.python.psi.*; import com.jetbrains.python.impl.psi.types.PyTupleType; +import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.types.PyType; import consulo.annotation.component.ExtensionImpl; import consulo.language.editor.inspection.LocalInspectionToolSession; import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.psi.PsiElementVisitor; -import org.jetbrains.annotations.Nls; - +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -34,72 +33,73 @@ */ @ExtensionImpl public class PyTupleAssignmentBalanceInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.incorrect.assignment"); - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } - - private static class Visitor extends PyInspectionVisitor { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameIncorrectAssignment(); } + @Nonnull @Override - public void visitPyAssignmentStatement(PyAssignmentStatement node) { - PyExpression lhsExpression = node.getLeftHandSideExpression(); - PyExpression assignedValue = node.getAssignedValue(); - if (lhsExpression instanceof PyParenthesizedExpression) // PY-4360 - { - lhsExpression = ((PyParenthesizedExpression)lhsExpression).getContainedExpression(); - } + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); + } - if (assignedValue == null) { - return; - } - PyType type = myTypeEvalContext.getType(assignedValue); - if (assignedValue instanceof PyReferenceExpression && !(type instanceof PyTupleType)) { - return; - } - if (lhsExpression instanceof PyTupleExpression && type != null) { - int valuesLength = PyUtil.getElementsCount(assignedValue, myTypeEvalContext); - if (valuesLength == -1) { - return; + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); } - PyExpression[] elements = ((PyTupleExpression)lhsExpression).getElements(); - boolean containsStarExpression = false; - if (LanguageLevel.forElement(node).isPy3K()) { - for (PyExpression target : elements) { - if (target instanceof PyStarExpression) { - if (containsStarExpression) { - registerProblem(target, "Only one starred expression allowed in assignment"); + @Override + public void visitPyAssignmentStatement(PyAssignmentStatement node) { + PyExpression lhsExpression = node.getLeftHandSideExpression(); + PyExpression assignedValue = node.getAssignedValue(); + if (lhsExpression instanceof PyParenthesizedExpression) // PY-4360 + { + lhsExpression = ((PyParenthesizedExpression) lhsExpression).getContainedExpression(); + } + + if (assignedValue == null) { return; - } - containsStarExpression = true; - ++valuesLength; } - } - } + PyType type = myTypeEvalContext.getType(assignedValue); + if (assignedValue instanceof PyReferenceExpression && !(type instanceof PyTupleType)) { + return; + } + if (lhsExpression instanceof PyTupleExpression && type != null) { + int valuesLength = PyUtil.getElementsCount(assignedValue, myTypeEvalContext); + if (valuesLength == -1) { + return; + } + PyExpression[] elements = ((PyTupleExpression) lhsExpression).getElements(); - int targetsLength = elements.length; - if (targetsLength > valuesLength) { - registerProblem(assignedValue, "Need more values to unpack"); - } - else if (!containsStarExpression && targetsLength < valuesLength) { - registerProblem(assignedValue, "Too many values to unpack"); + boolean containsStarExpression = false; + if (LanguageLevel.forElement(node).isPy3K()) { + for (PyExpression target : elements) { + if (target instanceof PyStarExpression) { + if (containsStarExpression) { + registerProblem(target, "Only one starred expression allowed in assignment"); + return; + } + containsStarExpression = true; + ++valuesLength; + } + } + } + + int targetsLength = elements.length; + if (targetsLength > valuesLength) { + registerProblem(assignedValue, "Need more values to unpack"); + } + else if (!containsStarExpression && targetsLength < valuesLength) { + registerProblem(assignedValue, "Too many values to unpack"); + } + } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTupleItemAssignmentInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTupleItemAssignmentInspection.java index 514c64f6..e90cbc42 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTupleItemAssignmentInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTupleItemAssignmentInspection.java @@ -15,21 +15,20 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.inspections.quickfix.PyReplaceTupleWithListQuickFix; +import com.jetbrains.python.impl.psi.types.PyTupleType; import com.jetbrains.python.psi.PyAssignmentStatement; import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.PyReferenceExpression; import com.jetbrains.python.psi.PySubscriptionExpression; -import com.jetbrains.python.impl.psi.types.PyTupleType; import com.jetbrains.python.psi.types.PyType; import consulo.annotation.component.ExtensionImpl; import consulo.language.editor.inspection.LocalInspectionToolSession; import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.psi.PsiElement; import consulo.language.psi.PsiElementVisitor; -import org.jetbrains.annotations.Nls; - +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; /** @@ -37,45 +36,46 @@ */ @ExtensionImpl public class PyTupleItemAssignmentInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.tuple.item.assignment"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameTupleItemAssignment(); + } - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } + @Nonnull + @Override + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); + } - private static class Visitor extends PyInspectionVisitor { + private static class Visitor extends PyInspectionVisitor { - public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { - super(holder, session); - } + public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { + super(holder, session); + } - @Override - public void visitPyAssignmentStatement(PyAssignmentStatement node) { - PyExpression[] targets = node.getTargets(); - if (targets.length == 1 && targets[0] instanceof PySubscriptionExpression) { - PySubscriptionExpression subscriptionExpression = (PySubscriptionExpression)targets[0]; - if (subscriptionExpression.getOperand() instanceof PyReferenceExpression) { - PyReferenceExpression referenceExpression = (PyReferenceExpression)subscriptionExpression.getOperand(); - PsiElement element = referenceExpression.followAssignmentsChain(getResolveContext()).getElement(); - if (element instanceof PyExpression) { - PyExpression expression = (PyExpression)element; - PyType type = myTypeEvalContext.getType(expression); - if (type instanceof PyTupleType) { - registerProblem(node, PyBundle.message("INSP.tuples.never.assign.items"), new PyReplaceTupleWithListQuickFix()); + @Override + public void visitPyAssignmentStatement(PyAssignmentStatement node) { + PyExpression[] targets = node.getTargets(); + if (targets.length == 1 && targets[0] instanceof PySubscriptionExpression) { + PySubscriptionExpression subscriptionExpression = (PySubscriptionExpression) targets[0]; + if (subscriptionExpression.getOperand() instanceof PyReferenceExpression) { + PyReferenceExpression referenceExpression = (PyReferenceExpression) subscriptionExpression.getOperand(); + PsiElement element = referenceExpression.followAssignmentsChain(getResolveContext()).getElement(); + if (element instanceof PyExpression) { + PyExpression expression = (PyExpression) element; + PyType type = myTypeEvalContext.getType(expression); + if (type instanceof PyTupleType) { + registerProblem(node, PyLocalize.inspTuplesNeverAssignItems().get(), new PyReplaceTupleWithListQuickFix()); + } + } + } } - } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTypeCheckerInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTypeCheckerInspection.java index 5b602a9b..4095c8d5 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTypeCheckerInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyTypeCheckerInspection.java @@ -24,20 +24,22 @@ import com.jetbrains.python.impl.psi.PyUtil; import com.jetbrains.python.impl.psi.types.*; import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.types.*; +import com.jetbrains.python.psi.types.PyClassLikeType; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.TypeEvalContext; import consulo.annotation.component.ExtensionImpl; import consulo.language.editor.inspection.LocalInspectionToolSession; import consulo.language.editor.inspection.ProblemHighlightType; import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.psi.PsiElementVisitor; +import consulo.localize.LocalizeValue; import consulo.logging.Logger; import consulo.util.dataholder.Key; import consulo.util.lang.Pair; import consulo.util.lang.StringUtil; -import org.jetbrains.annotations.Nls; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; + import java.util.*; /** @@ -45,254 +47,282 @@ */ @ExtensionImpl public class PyTypeCheckerInspection extends PyInspection { - private static final Logger LOG = Logger.getInstance(PyTypeCheckerInspection.class); - private static Key TIME_KEY = Key.create("PyTypeCheckerInspection.StartTime"); - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - if (LOG.isDebugEnabled()) { - session.putUserData(TIME_KEY, System.nanoTime()); - } - return new Visitor(holder, session); - } + private static final Logger LOG = Logger.getInstance(PyTypeCheckerInspection.class); + private static Key TIME_KEY = Key.create("PyTypeCheckerInspection.StartTime"); - public static class Visitor extends PyInspectionVisitor { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); - } - - // TODO: Visit decorators with arguments + @Nonnull @Override - public void visitPyCallExpression(PyCallExpression node) { - checkCallSite(node); + public LocalizeValue getDisplayName() { + return LocalizeValue.localizeTODO("Type checker"); } + @Nonnull @Override - public void visitPyBinaryExpression(PyBinaryExpression node) { - checkCallSite(node); + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + if (LOG.isDebugEnabled()) { + session.putUserData(TIME_KEY, System.nanoTime()); + } + return new Visitor(holder, session); } - @Override - public void visitPySubscriptionExpression(PySubscriptionExpression node) { - // TODO: Support slice PySliceExpressions - checkCallSite(node); - } + public static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } - @Override - public void visitPyForStatement(PyForStatement node) { - checkIteratedValue(node.getForPart().getSource(), node.isAsync()); - } + // TODO: Visit decorators with arguments + @Override + public void visitPyCallExpression(PyCallExpression node) { + checkCallSite(node); + } - @Override - public void visitPyReturnStatement(PyReturnStatement node) { - final ScopeOwner owner = ScopeUtil.getScopeOwner(node); - if (owner instanceof PyFunction) { - final PyFunction function = (PyFunction)owner; - final PyAnnotation annotation = function.getAnnotation(); - final String typeCommentAnnotation = function.getTypeCommentAnnotation(); - if (annotation != null || typeCommentAnnotation != null) { - final PyExpression returnExpr = node.getExpression(); - final PyType actual = returnExpr != null ? myTypeEvalContext.getType(returnExpr) : PyNoneType.INSTANCE; - final PyType expected = getExpectedReturnType(function); - if (!PyTypeChecker.match(expected, actual, myTypeEvalContext)) { - final String expectedName = PythonDocumentationProvider.getTypeName(expected, myTypeEvalContext); - final String actualName = PythonDocumentationProvider.getTypeName(actual, myTypeEvalContext); - PyMakeFunctionReturnTypeQuickFix localQuickFix = new PyMakeFunctionReturnTypeQuickFix(function, actualName, myTypeEvalContext); - PyMakeFunctionReturnTypeQuickFix globalQuickFix = new PyMakeFunctionReturnTypeQuickFix(function, null, myTypeEvalContext); - registerProblem(returnExpr != null ? returnExpr : node, + @Override + public void visitPyBinaryExpression(PyBinaryExpression node) { + checkCallSite(node); + } + + @Override + public void visitPySubscriptionExpression(PySubscriptionExpression node) { + // TODO: Support slice PySliceExpressions + checkCallSite(node); + } + + @Override + public void visitPyForStatement(PyForStatement node) { + checkIteratedValue(node.getForPart().getSource(), node.isAsync()); + } + + @Override + public void visitPyReturnStatement(PyReturnStatement node) { + final ScopeOwner owner = ScopeUtil.getScopeOwner(node); + if (owner instanceof PyFunction) { + final PyFunction function = (PyFunction) owner; + final PyAnnotation annotation = function.getAnnotation(); + final String typeCommentAnnotation = function.getTypeCommentAnnotation(); + if (annotation != null || typeCommentAnnotation != null) { + final PyExpression returnExpr = node.getExpression(); + final PyType actual = returnExpr != null ? myTypeEvalContext.getType(returnExpr) : PyNoneType.INSTANCE; + final PyType expected = getExpectedReturnType(function); + if (!PyTypeChecker.match(expected, actual, myTypeEvalContext)) { + final String expectedName = PythonDocumentationProvider.getTypeName(expected, myTypeEvalContext); + final String actualName = PythonDocumentationProvider.getTypeName(actual, myTypeEvalContext); + PyMakeFunctionReturnTypeQuickFix localQuickFix = + new PyMakeFunctionReturnTypeQuickFix(function, actualName, myTypeEvalContext); + PyMakeFunctionReturnTypeQuickFix globalQuickFix = + new PyMakeFunctionReturnTypeQuickFix(function, null, myTypeEvalContext); + registerProblem( + returnExpr != null ? returnExpr : node, String.format("Expected type '%s', got '%s' instead", expectedName, actualName), localQuickFix, - globalQuickFix); - } + globalQuickFix + ); + } + } + } } - } - } - @Nullable - private PyType getExpectedReturnType(@Nonnull PyFunction function) { - final PyType returnType = myTypeEvalContext.getReturnType(function); + @Nullable + private PyType getExpectedReturnType(@Nonnull PyFunction function) { + final PyType returnType = myTypeEvalContext.getReturnType(function); - if (returnType instanceof PyCollectionType && PyNames.FAKE_COROUTINE.equals(returnType.getName())) { - return ((PyCollectionType)returnType).getIteratedItemType(); - } + if (returnType instanceof PyCollectionType && PyNames.FAKE_COROUTINE.equals(returnType.getName())) { + return ((PyCollectionType) returnType).getIteratedItemType(); + } - return returnType; - } + return returnType; + } - @Override - public void visitPyFunction(PyFunction node) { - final PyAnnotation annotation = node.getAnnotation(); - final String typeCommentAnnotation = node.getTypeCommentAnnotation(); - if (annotation != null || typeCommentAnnotation != null) { - if (!PyUtil.isEmptyFunction(node)) { - final PyStatementList statements = node.getStatementList(); - ReturnVisitor visitor = new ReturnVisitor(node); - statements.accept(visitor); - if (!visitor.myHasReturns) { - final PyType expected = myTypeEvalContext.getReturnType(node); - final String expectedName = PythonDocumentationProvider.getTypeName(expected, myTypeEvalContext); - if (expected != null && !(expected instanceof PyNoneType)) { - registerProblem(annotation != null ? annotation.getValue() : node.getTypeComment(), - String.format("Expected to return '%s', got no return", expectedName)); + @Override + public void visitPyFunction(PyFunction node) { + final PyAnnotation annotation = node.getAnnotation(); + final String typeCommentAnnotation = node.getTypeCommentAnnotation(); + if (annotation != null || typeCommentAnnotation != null) { + if (!PyUtil.isEmptyFunction(node)) { + final PyStatementList statements = node.getStatementList(); + ReturnVisitor visitor = new ReturnVisitor(node); + statements.accept(visitor); + if (!visitor.myHasReturns) { + final PyType expected = myTypeEvalContext.getReturnType(node); + final String expectedName = PythonDocumentationProvider.getTypeName(expected, myTypeEvalContext); + if (expected != null && !(expected instanceof PyNoneType)) { + registerProblem( + annotation != null ? annotation.getValue() : node.getTypeComment(), + String.format("Expected to return '%s', got no return", expectedName) + ); + } + } + } } - } } - } - } - @Override - public void visitPyComprehensionElement(PyComprehensionElement node) { - super.visitPyComprehensionElement(node); + @Override + public void visitPyComprehensionElement(PyComprehensionElement node) { + super.visitPyComprehensionElement(node); - for (PyComprehensionForComponent forComponent : node.getForComponents()) { - checkIteratedValue(forComponent.getIteratedList(), forComponent.isAsync()); - } - } + for (PyComprehensionForComponent forComponent : node.getForComponents()) { + checkIteratedValue(forComponent.getIteratedList(), forComponent.isAsync()); + } + } - private static class ReturnVisitor extends PyRecursiveElementVisitor { - private final PyFunction myFunction; - private boolean myHasReturns = false; + private static class ReturnVisitor extends PyRecursiveElementVisitor { + private final PyFunction myFunction; + private boolean myHasReturns = false; - public ReturnVisitor(PyFunction function) { - myFunction = function; - } + public ReturnVisitor(PyFunction function) { + myFunction = function; + } - @Override - public void visitPyReturnStatement(PyReturnStatement node) { - if (ScopeUtil.getScopeOwner(node) == myFunction) { - myHasReturns = true; + @Override + public void visitPyReturnStatement(PyReturnStatement node) { + if (ScopeUtil.getScopeOwner(node) == myFunction) { + myHasReturns = true; + } + } } - } - } - private void checkCallSite(@Nullable PyCallSiteExpression callSite) { - final List resultsSet = PyTypeChecker.analyzeCallSite(callSite, myTypeEvalContext); - final List>> problemsSet = new ArrayList<>(); - for (PyTypeChecker.AnalyzeCallResults results : resultsSet) { - problemsSet.add(checkMapping(results.getReceiver(), results.getArguments())); - } - if (!problemsSet.isEmpty()) { - Map> minProblems = Collections.min(problemsSet, (o1, o2) -> o1.size() - o2.size()); - for (Map.Entry> entry : minProblems.entrySet()) { - registerProblem(entry.getKey(), entry.getValue().getFirst(), entry.getValue().getSecond()); + private void checkCallSite(@Nullable PyCallSiteExpression callSite) { + final List resultsSet = PyTypeChecker.analyzeCallSite(callSite, myTypeEvalContext); + final List>> problemsSet = new ArrayList<>(); + for (PyTypeChecker.AnalyzeCallResults results : resultsSet) { + problemsSet.add(checkMapping(results.getReceiver(), results.getArguments())); + } + if (!problemsSet.isEmpty()) { + Map> minProblems = + Collections.min(problemsSet, (o1, o2) -> o1.size() - o2.size()); + for (Map.Entry> entry : minProblems.entrySet()) { + registerProblem(entry.getKey(), entry.getValue().getFirst(), entry.getValue().getSecond()); + } + } } - } - } - private void checkIteratedValue(@Nullable PyExpression iteratedValue, boolean isAsync) { - if (iteratedValue != null) { - final PyType type = myTypeEvalContext.getType(iteratedValue); - final String iterableClassName = isAsync ? PyNames.ASYNC_ITERABLE : PyNames.ITERABLE; + private void checkIteratedValue(@Nullable PyExpression iteratedValue, boolean isAsync) { + if (iteratedValue != null) { + final PyType type = myTypeEvalContext.getType(iteratedValue); + final String iterableClassName = isAsync ? PyNames.ASYNC_ITERABLE : PyNames.ITERABLE; - if (type != null && !PyTypeChecker.isUnknown(type) && !PyABCUtil.isSubtype(type, iterableClassName, myTypeEvalContext)) { - final String typeName = PythonDocumentationProvider.getTypeName(type, myTypeEvalContext); + if (type != null && !PyTypeChecker.isUnknown(type) && !PyABCUtil.isSubtype(type, iterableClassName, myTypeEvalContext)) { + final String typeName = PythonDocumentationProvider.getTypeName(type, myTypeEvalContext); - registerProblem(iteratedValue, String.format("Expected 'collections.%s', got '%s' instead", iterableClassName, typeName)); + registerProblem( + iteratedValue, + String.format("Expected 'collections.%s', got '%s' instead", iterableClassName, typeName) + ); + } + } } - } - } - @Nonnull - private Map> checkMapping(@Nullable PyExpression receiver, - @Nonnull Map mapping) { - final Map> problems = new HashMap<>(); - final Map substitutions = new LinkedHashMap<>(); - boolean genericsCollected = false; - for (Map.Entry entry : mapping.entrySet()) { - final PyNamedParameter param = entry.getValue(); - final PyExpression arg = entry.getKey(); - if (param.isPositionalContainer() || param.isKeywordContainer()) { - continue; - } - final PyType paramType = myTypeEvalContext.getType(param); - if (paramType == null) { - continue; - } - final PyType argType = myTypeEvalContext.getType(arg); - if (!genericsCollected) { - substitutions.putAll(PyTypeChecker.unifyReceiver(receiver, myTypeEvalContext)); - genericsCollected = true; - } - final Pair problem = checkTypes(paramType, argType, myTypeEvalContext, substitutions); - if (problem != null) { - problems.put(arg, problem); + @Nonnull + private Map> checkMapping( + @Nullable PyExpression receiver, + @Nonnull Map mapping + ) { + final Map> problems = new HashMap<>(); + final Map substitutions = new LinkedHashMap<>(); + boolean genericsCollected = false; + for (Map.Entry entry : mapping.entrySet()) { + final PyNamedParameter param = entry.getValue(); + final PyExpression arg = entry.getKey(); + if (param.isPositionalContainer() || param.isKeywordContainer()) { + continue; + } + final PyType paramType = myTypeEvalContext.getType(param); + if (paramType == null) { + continue; + } + final PyType argType = myTypeEvalContext.getType(arg); + if (!genericsCollected) { + substitutions.putAll(PyTypeChecker.unifyReceiver(receiver, myTypeEvalContext)); + genericsCollected = true; + } + final Pair problem = checkTypes(paramType, argType, myTypeEvalContext, substitutions); + if (problem != null) { + problems.put(arg, problem); + } + } + return problems; } - } - return problems; - } - @Nullable - private static Pair checkTypes(@Nullable PyType expected, - @Nullable PyType actual, - @Nonnull TypeEvalContext context, - @Nonnull Map substitutions) { - if (actual != null && expected != null) { - if (!PyTypeChecker.match(expected, actual, context, substitutions)) { - final String expectedName = PythonDocumentationProvider.getTypeName(expected, context); - String quotedExpectedName = String.format("'%s'", expectedName); - final boolean hasGenerics = PyTypeChecker.hasGenerics(expected, context); - ProblemHighlightType highlightType = ProblemHighlightType.GENERIC_ERROR_OR_WARNING; - if (hasGenerics) { - final PyType substitute = PyTypeChecker.substitute(expected, substitutions, context); - if (substitute != null) { - quotedExpectedName = String.format("'%s' (matched generic type '%s')", - PythonDocumentationProvider.getTypeName(substitute, context), - expectedName); - highlightType = ProblemHighlightType.WEAK_WARNING; - } - } - final String actualName = PythonDocumentationProvider.getTypeName(actual, context); - String msg = String.format("Expected type %s, got '%s' instead", quotedExpectedName, actualName); - if (expected instanceof PyStructuralType) { - final Set expectedAttributes = ((PyStructuralType)expected).getAttributeNames(); - final Set actualAttributes = getAttributes(actual, context); - if (actualAttributes != null) { - final Sets.SetView missingAttributes = Sets.difference(expectedAttributes, actualAttributes); - if (missingAttributes.size() == 1) { - msg = String.format("Type '%s' doesn't have expected attribute '%s'", actualName, missingAttributes.iterator().next()); - } - else { - msg = String.format("Type '%s' doesn't have expected attributes %s", + @Nullable + private static Pair checkTypes( + @Nullable PyType expected, + @Nullable PyType actual, + @Nonnull TypeEvalContext context, + @Nonnull Map substitutions + ) { + if (actual != null && expected != null) { + if (!PyTypeChecker.match(expected, actual, context, substitutions)) { + final String expectedName = PythonDocumentationProvider.getTypeName(expected, context); + String quotedExpectedName = String.format("'%s'", expectedName); + final boolean hasGenerics = PyTypeChecker.hasGenerics(expected, context); + ProblemHighlightType highlightType = ProblemHighlightType.GENERIC_ERROR_OR_WARNING; + if (hasGenerics) { + final PyType substitute = PyTypeChecker.substitute(expected, substitutions, context); + if (substitute != null) { + quotedExpectedName = String.format( + "'%s' (matched generic type '%s')", + PythonDocumentationProvider.getTypeName(substitute, context), + expectedName + ); + highlightType = ProblemHighlightType.WEAK_WARNING; + } + } + final String actualName = PythonDocumentationProvider.getTypeName(actual, context); + String msg = String.format("Expected type %s, got '%s' instead", quotedExpectedName, actualName); + if (expected instanceof PyStructuralType) { + final Set expectedAttributes = ((PyStructuralType) expected).getAttributeNames(); + final Set actualAttributes = getAttributes(actual, context); + if (actualAttributes != null) { + final Sets.SetView missingAttributes = Sets.difference(expectedAttributes, actualAttributes); + if (missingAttributes.size() == 1) { + msg = String.format( + "Type '%s' doesn't have expected attribute '%s'", + actualName, + missingAttributes.iterator().next() + ); + } + else { + msg = String.format( + "Type '%s' doesn't have expected attributes %s", actualName, - StringUtil.join(missingAttributes, s -> String.format("'%s'", s), ", ")); - } + StringUtil.join(missingAttributes, s -> String.format("'%s'", s), ", ") + ); + } + } + } + return Pair.create(msg, highlightType); + } } - } - return Pair.create(msg, highlightType); + return null; } - } - return null; } - } - @Nullable - private static Set getAttributes(@Nonnull PyType type, @Nonnull TypeEvalContext context) { - if (type instanceof PyStructuralType) { - return ((PyStructuralType)type).getAttributeNames(); - } - else if (type instanceof PyClassLikeType) { - return ((PyClassLikeType)type).getMemberNames(true, context); + @Nullable + private static Set getAttributes(@Nonnull PyType type, @Nonnull TypeEvalContext context) { + if (type instanceof PyStructuralType) { + return ((PyStructuralType) type).getAttributeNames(); + } + else if (type instanceof PyClassLikeType) { + return ((PyClassLikeType) type).getMemberNames(true, context); + } + return null; } - return null; - } - @Override - public void inspectionFinished(@Nonnull LocalInspectionToolSession session, @Nonnull ProblemsHolder problemsHolder) { - if (LOG.isDebugEnabled()) { - final Long startTime = session.getUserData(TIME_KEY); - if (startTime != null) { - LOG.debug(String.format("[%d] elapsed time: %d ms\n", Thread.currentThread().getId(), (System.nanoTime() - startTime) / 1000000)); - } + @Override + public void inspectionFinished(@Nonnull LocalInspectionToolSession session, @Nonnull ProblemsHolder problemsHolder) { + if (LOG.isDebugEnabled()) { + final Long startTime = session.getUserData(TIME_KEY); + if (startTime != null) { + LOG.debug(String.format( + "[%d] elapsed time: %d ms\n", + Thread.currentThread().getId(), + (System.nanoTime() - startTime) / 1000000 + )); + } + } } - } - - @Nls - @Nonnull - public String getDisplayName() { - return "Type checker"; - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnboundLocalVariableInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnboundLocalVariableInspection.java index d79c9e36..324073bc 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnboundLocalVariableInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnboundLocalVariableInspection.java @@ -16,7 +16,6 @@ package com.jetbrains.python.impl.inspections; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.impl.codeInsight.controlflow.ReadWriteInstruction; import com.jetbrains.python.impl.codeInsight.dataflow.scope.Scope; @@ -39,11 +38,12 @@ import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiPolyVariantReference; import consulo.language.psi.util.PsiTreeUtil; +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import consulo.util.dataholder.Key; import consulo.util.lang.ref.Ref; -import org.jetbrains.annotations.Nls; - import jakarta.annotation.Nonnull; + import java.util.HashSet; import java.util.Set; @@ -52,170 +52,180 @@ */ @ExtensionImpl public class PyUnboundLocalVariableInspection extends PyInspection { - private static Key> LARGE_FUNCTIONS_KEY = Key.create("PyUnboundLocalVariableInspection.LargeFunctions"); - - @Nonnull - @Nls - public String getDisplayName() { - return PyBundle.message("INSP.NAME.unbound"); - } + private static Key> LARGE_FUNCTIONS_KEY = Key.create("PyUnboundLocalVariableInspection.LargeFunctions"); - @Nonnull - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull final LocalInspectionToolSession session) { - session.putUserData(LARGE_FUNCTIONS_KEY, new HashSet<>()); - return new Visitor(holder, session); - } - - public static class Visitor extends PyInspectionVisitor { + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameUnbound(); + } - public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { - super(holder, session); + @Nonnull + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull final LocalInspectionToolSession session + ) { + session.putUserData(LARGE_FUNCTIONS_KEY, new HashSet<>()); + return new Visitor(holder, session); } - @Override - public void visitPyReferenceExpression(final PyReferenceExpression node) { - if (node.getContainingFile() instanceof PyExpressionCodeFragment) { - return; - } - // Ignore global statements arguments - if (PyGlobalStatementNavigator.getByArgument(node) != null) { - return; - } - // Ignore qualifier inspections - if (node.isQualified()) { - return; - } - // Ignore import subelements - if (PsiTreeUtil.getParentOfType(node, PyImportStatementBase.class) != null) { - return; - } - final String name = node.getReferencedName(); - if (name == null) { - return; - } - final ScopeOwner owner = ScopeUtil.getDeclarationScopeOwner(node, name); - final Set largeFunctions = getSession().getUserData(LARGE_FUNCTIONS_KEY); - assert largeFunctions != null; - if (owner == null || largeFunctions.contains(owner)) { - return; - } - // Ignore references declared in outer scopes - if (owner != ScopeUtil.getScopeOwner(node)) { - return; - } - final Scope scope = ControlFlowCache.getScope(owner); - // Ignore globals and if scope even doesn't contain such a declaration - if (scope.isGlobal(name) || (!scope.containsDeclaration(name))) { - return; - } - // Start DFA from the assignment statement in case of augmented assignments - final PsiElement anchor; - final PyAugAssignmentStatement augAssignment = PsiTreeUtil.getParentOfType(node, PyAugAssignmentStatement.class); - if (augAssignment != null && name.equals(augAssignment.getTarget().getName())) { - anchor = augAssignment; - } - else { - anchor = node; - } - final ScopeVariable variable; - try { - variable = scope.getDeclaredVariable(anchor, name); - } - catch (DFALimitExceededException e) { - largeFunctions.add(owner); - registerLargeFunction(owner); - return; - } - if (variable == null) { - if (!isFirstUnboundRead(node, owner)) { - return; - } - final PsiPolyVariantReference ref = node.getReference(getResolveContext()); - if (ref == null) { - return; - } - final PsiElement resolved = ref.resolve(); - final boolean isBuiltin = PyBuiltinCache.getInstance(node).isBuiltin(resolved); - if (owner instanceof PyClass) { - if (isBuiltin || ScopeUtil.getDeclarationScopeOwner(owner, name) != null) { - return; - } - } - if (PyUnreachableCodeInspection.hasAnyInterruptedControlFlowPaths(node)) { - return; - } - if (owner instanceof PyFile) { - if (isBuiltin) { - return; - } - if (resolved != null && !PyUtil.inSameFile(node, resolved)) { - return; - } - registerProblem(node, PyBundle.message("INSP.unbound.name.not.defined", name)); - } - else { - registerProblem(node, - PyBundle.message("INSP.unbound.local.variable", node.getName()), - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - null, - new AddGlobalQuickFix()); + public static class Visitor extends PyInspectionVisitor { + + public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { + super(holder, session); } - } - } - private static boolean isFirstUnboundRead(@Nonnull PyReferenceExpression node, @Nonnull ScopeOwner owner) { - final String nodeName = node.getReferencedName(); - final Scope scope = ControlFlowCache.getScope(owner); - final ControlFlow flow = ControlFlowCache.getControlFlow(owner); - final Instruction[] instructions = flow.getInstructions(); - final int num = ControlFlowUtil.findInstructionNumberByElement(instructions, node); - if (num < 0) { - return true; - } - final Ref first = Ref.create(true); - ControlFlowUtil.iteratePrev(num, instructions, instruction -> { - if (instruction instanceof ReadWriteInstruction) { - final ReadWriteInstruction rwInstruction = (ReadWriteInstruction)instruction; - final String name = rwInstruction.getName(); - final PsiElement element = rwInstruction.getElement(); - if (element != null && name != null && name.equals(nodeName) && instruction.num() != num) { + @Override + public void visitPyReferenceExpression(final PyReferenceExpression node) { + if (node.getContainingFile() instanceof PyExpressionCodeFragment) { + return; + } + // Ignore global statements arguments + if (PyGlobalStatementNavigator.getByArgument(node) != null) { + return; + } + // Ignore qualifier inspections + if (node.isQualified()) { + return; + } + // Ignore import subelements + if (PsiTreeUtil.getParentOfType(node, PyImportStatementBase.class) != null) { + return; + } + final String name = node.getReferencedName(); + if (name == null) { + return; + } + final ScopeOwner owner = ScopeUtil.getDeclarationScopeOwner(node, name); + final Set largeFunctions = getSession().getUserData(LARGE_FUNCTIONS_KEY); + assert largeFunctions != null; + if (owner == null || largeFunctions.contains(owner)) { + return; + } + // Ignore references declared in outer scopes + if (owner != ScopeUtil.getScopeOwner(node)) { + return; + } + final Scope scope = ControlFlowCache.getScope(owner); + // Ignore globals and if scope even doesn't contain such a declaration + if (scope.isGlobal(name) || (!scope.containsDeclaration(name))) { + return; + } + // Start DFA from the assignment statement in case of augmented assignments + final PsiElement anchor; + final PyAugAssignmentStatement augAssignment = PsiTreeUtil.getParentOfType(node, PyAugAssignmentStatement.class); + if (augAssignment != null && name.equals(augAssignment.getTarget().getName())) { + anchor = augAssignment; + } + else { + anchor = node; + } + final ScopeVariable variable; try { - if (scope.getDeclaredVariable(element, name) == null) { - final ReadWriteInstruction.ACCESS access = rwInstruction.getAccess(); - if (access.isReadAccess()) { - first.set(false); - return ControlFlowUtil.Operation.BREAK; - } - } + variable = scope.getDeclaredVariable(anchor, name); } catch (DFALimitExceededException e) { - first.set(false); + largeFunctions.add(owner); + registerLargeFunction(owner); + return; + } + if (variable == null) { + if (!isFirstUnboundRead(node, owner)) { + return; + } + final PsiPolyVariantReference ref = node.getReference(getResolveContext()); + if (ref == null) { + return; + } + final PsiElement resolved = ref.resolve(); + final boolean isBuiltin = PyBuiltinCache.getInstance(node).isBuiltin(resolved); + if (owner instanceof PyClass) { + if (isBuiltin || ScopeUtil.getDeclarationScopeOwner(owner, name) != null) { + return; + } + } + if (PyUnreachableCodeInspection.hasAnyInterruptedControlFlowPaths(node)) { + return; + } + if (owner instanceof PyFile) { + if (isBuiltin) { + return; + } + if (resolved != null && !PyUtil.inSameFile(node, resolved)) { + return; + } + registerProblem(node, PyLocalize.inspUnboundNameNotDefined(name).get()); + } + else { + registerProblem( + node, + PyLocalize.inspUnboundLocalVariable(node.getName()).get(), + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + null, + new AddGlobalQuickFix() + ); + } } - return ControlFlowUtil.Operation.CONTINUE; - } } - return ControlFlowUtil.Operation.NEXT; - }); - return first.get(); - } - @Override - public void visitPyNonlocalStatement(final PyNonlocalStatement node) { - for (PyTargetExpression var : node.getVariables()) { - final String name = var.getName(); - final ScopeOwner owner = ScopeUtil.getDeclarationScopeOwner(var, name); - if (owner == null || owner instanceof PyFile) { - registerProblem(var, PyBundle.message("INSP.unbound.nonlocal.variable", name), ProblemHighlightType.GENERIC_ERROR_OR_WARNING); + private static boolean isFirstUnboundRead(@Nonnull PyReferenceExpression node, @Nonnull ScopeOwner owner) { + final String nodeName = node.getReferencedName(); + final Scope scope = ControlFlowCache.getScope(owner); + final ControlFlow flow = ControlFlowCache.getControlFlow(owner); + final Instruction[] instructions = flow.getInstructions(); + final int num = ControlFlowUtil.findInstructionNumberByElement(instructions, node); + if (num < 0) { + return true; + } + final Ref first = Ref.create(true); + ControlFlowUtil.iteratePrev(num, instructions, instruction -> { + if (instruction instanceof ReadWriteInstruction) { + final ReadWriteInstruction rwInstruction = (ReadWriteInstruction) instruction; + final String name = rwInstruction.getName(); + final PsiElement element = rwInstruction.getElement(); + if (element != null && name != null && name.equals(nodeName) && instruction.num() != num) { + try { + if (scope.getDeclaredVariable(element, name) == null) { + final ReadWriteInstruction.ACCESS access = rwInstruction.getAccess(); + if (access.isReadAccess()) { + first.set(false); + return ControlFlowUtil.Operation.BREAK; + } + } + } + catch (DFALimitExceededException e) { + first.set(false); + } + return ControlFlowUtil.Operation.CONTINUE; + } + } + return ControlFlowUtil.Operation.NEXT; + }); + return first.get(); } - } - } - private void registerLargeFunction(ScopeOwner owner) { - registerProblem((owner instanceof PyFunction) ? ((PyFunction)owner).getNameIdentifier() : owner, - PyBundle.message("INSP.unbound.function.too.large", owner.getName()), - ProblemHighlightType.WEAK_WARNING); + @Override + public void visitPyNonlocalStatement(final PyNonlocalStatement node) { + for (PyTargetExpression var : node.getVariables()) { + final String name = var.getName(); + final ScopeOwner owner = ScopeUtil.getDeclarationScopeOwner(var, name); + if (owner == null || owner instanceof PyFile) { + registerProblem( + var, + PyLocalize.inspUnboundNonlocalVariable(name).get(), + ProblemHighlightType.GENERIC_ERROR_OR_WARNING + ); + } + } + } + + private void registerLargeFunction(ScopeOwner owner) { + registerProblem( + (owner instanceof PyFunction) ? ((PyFunction) owner).getNameIdentifier() : owner, + PyLocalize.inspUnboundFunctionTooLarge(owner.getName()).get(), + ProblemHighlightType.WEAK_WARNING + ); + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnnecessaryBackslashInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnnecessaryBackslashInspection.java index 7c8ffa5c..d7e0d0bc 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnnecessaryBackslashInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnnecessaryBackslashInspection.java @@ -13,10 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.inspections.quickfix.RemoveUnnecessaryBackslashQuickFix; import com.jetbrains.python.psi.*; import consulo.annotation.component.ExtensionImpl; @@ -26,109 +24,110 @@ import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiWhiteSpace; import consulo.language.psi.util.PsiTreeUtil; +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import consulo.util.collection.Stack; -import org.jetbrains.annotations.Nls; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; /** - * User: catherine - *

* Inspection to highlight backslashes in places where line continuation is implicit (inside (), [], {}). + * + * @author catherine */ @ExtensionImpl public class PyUnnecessaryBackslashInspection extends PyInspection { - - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.unnecessary.backslash"); - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } - - public static class Visitor extends PyInspectionVisitor { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); - } - + @Nonnull @Override - public void visitPyParameterList(final PyParameterList list) { - findProblem(list); + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameUnnecessaryBackslash(); } + @Nonnull @Override - public void visitPyArgumentList(final PyArgumentList list) { - findProblem(list); + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } - @Override - public void visitPyTupleExpression(PyTupleExpression node) { - if (node.getParent() instanceof PyParenthesizedExpression) - findProblem(node); - } + public static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } - @Override - public void visitPyParenthesizedExpression(final PyParenthesizedExpression expression) { - final Stack stack = new Stack(); - stack.push(expression); - while (!stack.isEmpty()) { - PsiElement element = stack.pop(); - if (!(element instanceof PyTupleExpression)) { - findProblem(element); - if (element != null) { - for (PsiElement psiElement : element.getChildren()) { - stack.push(psiElement); + @Override + public void visitPyParameterList(final PyParameterList list) { + findProblem(list); + } + + @Override + public void visitPyArgumentList(final PyArgumentList list) { + findProblem(list); + } + + @Override + public void visitPyTupleExpression(PyTupleExpression node) { + if (node.getParent() instanceof PyParenthesizedExpression) { + findProblem(node); } - } } - } - } - @Override - public void visitPyDictLiteralExpression(final PyDictLiteralExpression expression) { - findProblem(expression); - } + @Override + public void visitPyParenthesizedExpression(final PyParenthesizedExpression expression) { + final Stack stack = new Stack(); + stack.push(expression); + while (!stack.isEmpty()) { + PsiElement element = stack.pop(); + if (!(element instanceof PyTupleExpression)) { + findProblem(element); + if (element != null) { + for (PsiElement psiElement : element.getChildren()) { + stack.push(psiElement); + } + } + } + } + } - @Override - public void visitPyListLiteralExpression(final PyListLiteralExpression expression) { - findProblem(expression); - } + @Override + public void visitPyDictLiteralExpression(final PyDictLiteralExpression expression) { + findProblem(expression); + } - @Override - public void visitPySetLiteralExpression(final PySetLiteralExpression expression) { - findProblem(expression); - } + @Override + public void visitPyListLiteralExpression(final PyListLiteralExpression expression) { + findProblem(expression); + } - @Override - public void visitPyStringLiteralExpression(final PyStringLiteralExpression stringLiteralExpression) { - PsiElement parent = stringLiteralExpression.getParent(); - if (parent instanceof PyListLiteralExpression || parent instanceof PyParenthesizedExpression || - parent instanceof PySetLiteralExpression || parent instanceof PyKeyValueExpression || - parent instanceof PyNamedParameter || parent instanceof PyArgumentList) { - findProblem(stringLiteralExpression); - } - } + @Override + public void visitPySetLiteralExpression(final PySetLiteralExpression expression) { + findProblem(expression); + } - private void findProblem(@Nullable final PsiElement expression) { - final PsiWhiteSpace[] children = PsiTreeUtil.getChildrenOfType(expression, PsiWhiteSpace.class); - if (children != null) { - for (PsiWhiteSpace ws : children) { - if (ws.getText().contains("\\")) { - registerProblem(ws, "Unnecessary backslash in expression.", new RemoveUnnecessaryBackslashQuickFix()); - } + @Override + public void visitPyStringLiteralExpression(final PyStringLiteralExpression stringLiteralExpression) { + PsiElement parent = stringLiteralExpression.getParent(); + if (parent instanceof PyListLiteralExpression || parent instanceof PyParenthesizedExpression || + parent instanceof PySetLiteralExpression || parent instanceof PyKeyValueExpression || + parent instanceof PyNamedParameter || parent instanceof PyArgumentList) { + findProblem(stringLiteralExpression); + } } - } - } - } + private void findProblem(@Nullable final PsiElement expression) { + final PsiWhiteSpace[] children = PsiTreeUtil.getChildrenOfType(expression, PsiWhiteSpace.class); + if (children != null) { + for (PsiWhiteSpace ws : children) { + if (ws.getText().contains("\\")) { + registerProblem(ws, "Unnecessary backslash in expression.", new RemoveUnnecessaryBackslashQuickFix()); + } + } + } + } + + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnreachableCodeInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnreachableCodeInspection.java index da4cff01..67a9fc65 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnreachableCodeInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnreachableCodeInspection.java @@ -13,11 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.inspections; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.impl.codeInsight.dataflow.scope.ScopeUtil; import consulo.annotation.component.ExtensionImpl; @@ -28,10 +26,11 @@ import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.psi.PsiElement; import consulo.language.psi.PsiElementVisitor; +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import consulo.util.lang.ref.Ref; -import org.jetbrains.annotations.Nls; - import jakarta.annotation.Nonnull; + import java.util.ArrayList; import java.util.List; @@ -39,87 +38,72 @@ * Detects unreachable code using control flow graph */ @ExtensionImpl -public class PyUnreachableCodeInspection extends PyInspection -{ - @Nls - @Nonnull - public String getDisplayName() - { - return PyBundle.message("INSP.NAME.unreachable.code"); - } +public class PyUnreachableCodeInspection extends PyInspection { + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameUnreachableCode(); + } - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull final ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) - { - return new Visitor(holder, session); - } + @Nonnull + @Override + public PsiElementVisitor buildVisitor( + @Nonnull final ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); + } - public static class Visitor extends PyInspectionVisitor - { - public Visitor(@Nonnull ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) - { - super(holder, session); - } + public static class Visitor extends PyInspectionVisitor { + public Visitor(@Nonnull ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } - @Override - public void visitElement(final PsiElement element) - { - if(element instanceof ScopeOwner) - { - final ControlFlow flow = ControlFlowCache.getControlFlow((ScopeOwner) element); - final Instruction[] instructions = flow.getInstructions(); - final List unreachable = new ArrayList(); - if(instructions.length > 0) - { - ControlFlowUtil.iteratePrev(instructions.length - 1, instructions, instruction -> - { - if(instruction.allPred().isEmpty() && !isFirstInstruction(instruction)) - { - unreachable.add(instruction.getElement()); - } - return ControlFlowUtil.Operation.NEXT; - }); - } - for(PsiElement e : unreachable) - { - registerProblem(e, PyBundle.message("INSP.unreachable.code")); - } - } - } - } + @Override + public void visitElement(final PsiElement element) { + if (element instanceof ScopeOwner) { + final ControlFlow flow = ControlFlowCache.getControlFlow((ScopeOwner) element); + final Instruction[] instructions = flow.getInstructions(); + final List unreachable = new ArrayList(); + if (instructions.length > 0) { + ControlFlowUtil.iteratePrev(instructions.length - 1, instructions, instruction -> { + if (instruction.allPred().isEmpty() && !isFirstInstruction(instruction)) { + unreachable.add(instruction.getElement()); + } + return ControlFlowUtil.Operation.NEXT; + }); + } + for (PsiElement e : unreachable) { + registerProblem(e, PyLocalize.inspUnreachableCode().get()); + } + } + } + } - public static boolean hasAnyInterruptedControlFlowPaths(@Nonnull PsiElement element) - { - final ScopeOwner owner = ScopeUtil.getScopeOwner(element); - if(owner != null) - { - final ControlFlow flow = ControlFlowCache.getControlFlow(owner); - final Instruction[] instructions = flow.getInstructions(); - final int start = ControlFlowUtil.findInstructionNumberByElement(instructions, element); - if(start >= 0) - { - final Ref resultRef = Ref.create(false); - ControlFlowUtil.iteratePrev(start, instructions, instruction -> - { - if(instruction.allPred().isEmpty() && !isFirstInstruction(instruction)) - { - resultRef.set(true); - return ControlFlowUtil.Operation.BREAK; - } - return ControlFlowUtil.Operation.NEXT; - }); - return resultRef.get(); - } - } - return false; - } + public static boolean hasAnyInterruptedControlFlowPaths(@Nonnull PsiElement element) { + final ScopeOwner owner = ScopeUtil.getScopeOwner(element); + if (owner != null) { + final ControlFlow flow = ControlFlowCache.getControlFlow(owner); + final Instruction[] instructions = flow.getInstructions(); + final int start = ControlFlowUtil.findInstructionNumberByElement(instructions, element); + if (start >= 0) { + final Ref resultRef = Ref.create(false); + ControlFlowUtil.iteratePrev(start, instructions, instruction -> { + if (instruction.allPred().isEmpty() && !isFirstInstruction(instruction)) { + resultRef.set(true); + return ControlFlowUtil.Operation.BREAK; + } + return ControlFlowUtil.Operation.NEXT; + }); + return resultRef.get(); + } + } + return false; + } - private static boolean isFirstInstruction(Instruction instruction) - { - return instruction.num() == 0; - } + private static boolean isFirstInstruction(Instruction instruction) { + return instruction.num() == 0; + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnusedLocalInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnusedLocalInspection.java index fd8f3f27..0aec99b2 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnusedLocalInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyUnusedLocalInspection.java @@ -13,18 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import consulo.annotation.component.ExtensionImpl; import consulo.language.editor.inspection.InspectionToolState; import consulo.language.editor.inspection.LocalInspectionToolSession; import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.psi.PsiElementVisitor; +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import consulo.util.dataholder.Key; -import org.jetbrains.annotations.Nls; - import jakarta.annotation.Nonnull; /** @@ -32,48 +30,51 @@ */ @ExtensionImpl public class PyUnusedLocalInspection extends PyInspection { - private static Key KEY = Key.create("PyUnusedLocal.Visitor"); + private static Key KEY = Key.create("PyUnusedLocal.Visitor"); - @Nonnull - @Override - public InspectionToolState createStateProvider() { - return new PyUnusedLocalInspectionState(); - } + @Nonnull + @Override + public InspectionToolState createStateProvider() { + return new PyUnusedLocalInspectionState(); + } - @Override - @Nonnull - @Nls - public String getDisplayName() { - return PyBundle.message("INSP.NAME.unused"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameUnused(); + } - @Override - @Nonnull - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - final boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - PyUnusedLocalInspectionState inspectionState = (PyUnusedLocalInspectionState)state; + @Override + @Nonnull + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + final boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + PyUnusedLocalInspectionState inspectionState = (PyUnusedLocalInspectionState) state; - final PyUnusedLocalInspectionVisitor visitor = new PyUnusedLocalInspectionVisitor(holder, - session, - inspectionState.ignoreTupleUnpacking, - inspectionState.ignoreLambdaParameters, - inspectionState.ignoreLoopIterationVariables); - // buildVisitor() will be called on injected files in the same session - don't overwrite if we already have one - final PyUnusedLocalInspectionVisitor existingVisitor = session.getUserData(KEY); - if (existingVisitor == null) { - session.putUserData(KEY, visitor); + final PyUnusedLocalInspectionVisitor visitor = new PyUnusedLocalInspectionVisitor( + holder, + session, + inspectionState.ignoreTupleUnpacking, + inspectionState.ignoreLambdaParameters, + inspectionState.ignoreLoopIterationVariables + ); + // buildVisitor() will be called on injected files in the same session - don't overwrite if we already have one + final PyUnusedLocalInspectionVisitor existingVisitor = session.getUserData(KEY); + if (existingVisitor == null) { + session.putUserData(KEY, visitor); + } + return visitor; } - return visitor; - } - @Override - public void inspectionFinished(@Nonnull LocalInspectionToolSession session, @Nonnull ProblemsHolder holder, Object state) { - final PyUnusedLocalInspectionVisitor visitor = session.getUserData(KEY); - if (visitor != null) { - visitor.registerProblems(); - session.putUserData(KEY, null); + @Override + public void inspectionFinished(@Nonnull LocalInspectionToolSession session, @Nonnull ProblemsHolder holder, Object state) { + final PyUnusedLocalInspectionVisitor visitor = session.getUserData(KEY); + if (visitor != null) { + visitor.registerProblems(); + session.putUserData(KEY, null); + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/quickfix/PyFillParagraphFix.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/quickfix/PyFillParagraphFix.java index 968c0713..19e25855 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/quickfix/PyFillParagraphFix.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/quickfix/PyFillParagraphFix.java @@ -21,24 +21,24 @@ import consulo.language.editor.intention.HighPriorityAction; import consulo.language.psi.PsiFile; import consulo.language.util.IncorrectOperationException; +import consulo.localize.LocalizeValue; import consulo.project.Project; import jakarta.annotation.Nonnull; public class PyFillParagraphFix extends BaseIntentionAction implements HighPriorityAction { + public PyFillParagraphFix() { + setText(LocalizeValue.localizeTODO("Fill paragraph")); + } - public PyFillParagraphFix() { - setText("Fill paragraph"); - } + @Override + public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { + return true; + } - @Override - public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { - return true; - } - - @Override - public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - final FillParagraphAction action = new consulo.ide.impl.idea.codeInsight.editorActions.fillParagraph.FillParagraphAction(); - action.actionPerformedImpl(project, editor); - } + @Override + public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + final FillParagraphAction action = new consulo.ide.impl.idea.codeInsight.editorActions.fillParagraph.FillParagraphAction(); + action.actionPerformedImpl(project, editor); + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java index ca118d9b..231e0cf0 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java @@ -19,7 +19,6 @@ import com.jetbrains.python.PyNames; import com.jetbrains.python.codeInsight.PyCustomMember; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.PyCustomType; import com.jetbrains.python.impl.codeInsight.PyCodeInsightSettings; import com.jetbrains.python.impl.codeInsight.dataflow.scope.ScopeUtil; @@ -78,10 +77,9 @@ import consulo.util.dataholder.Key; import consulo.util.lang.Comparing; import consulo.util.lang.Pair; -import org.jetbrains.annotations.NonNls; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; +import org.jetbrains.annotations.NonNls; import java.util.*; @@ -89,6 +87,7 @@ /** * Marks references that fail to resolve. Also tracks unused imports and provides "optimize imports" support. + * * @author dcheryasov * @since 2008-11-15 */ @@ -458,7 +457,7 @@ private void processReferenceInImportGuard(PyElement node, PyExceptPart guard) { final PyElement toHighlight = asElement != null ? asElement : node; registerProblem( toHighlight, - PyBundle.message("INSP.try.except.import.error", visibleName), + PyLocalize.inspTryExceptImportError(visibleName).get(), ProblemHighlightType.LIKE_UNKNOWN_SYMBOL ); } @@ -474,7 +473,7 @@ private void registerUnresolvedReferenceProblem( if (reference instanceof DocStringTypeReference) { return; } - String description = null; + LocalizeValue description = LocalizeValue.empty(); PsiElement element = reference.getElement(); final String text = element.getText(); TextRange rangeInElement = reference.getRangeInElement(); @@ -555,14 +554,14 @@ else if (canonicalName.equals(ignored)) { PyIfStatement.class ) != null)) { severity = HighlightSeverity.WEAK_WARNING; - description = PyBundle.message("INSP.module.$0.not.found", refText); + description = PyLocalize.inspModule$0NotFound(refText); // TODO: mark the node so that future references pointing to it won't result in a error, but in a warning } } - if (reference instanceof PsiReferenceEx && description == null) { - description = ((PsiReferenceEx) reference).getUnresolvedDescription(); + if (reference instanceof PsiReferenceEx referenceEx && description == LocalizeValue.empty()) { + description = LocalizeValue.localizeTODO(referenceEx.getUnresolvedDescription()); } - if (description == null) { + if (description == LocalizeValue.empty()) { boolean markedQualified = false; if (element instanceof PyQualifiedExpression) { // TODO: Add __qualname__ for Python 3.3 to the skeleton of , introduce a pseudo-class skeleton for @@ -584,7 +583,7 @@ else if (canonicalName.equals(ignored)) { addCreateMemberFromUsageFixes(type, reference, refText, actions); if (type instanceof PyClassType) { final PyClassType classType = (PyClassType) type; - if (reference instanceof PyOperatorReference) { + if (reference instanceof PyOperatorReference operatorRef) { String className = type.getName(); if (classType.isDefinition()) { final PyClassLikeType metaClassType = classType.getMetaClassType(myTypeEvalContext, true); @@ -592,11 +591,10 @@ else if (canonicalName.equals(ignored)) { className = metaClassType.getName(); } } - description = PyBundle.message( - "INSP.unresolved.operator.ref", + description = PyLocalize.inspUnresolvedOperatorRef( className, refName, - ((PyOperatorReference) reference).getReadableOperatorName() + operatorRef.getReadableOperatorName() ); } else { @@ -606,7 +604,7 @@ else if (canonicalName.equals(ignored)) { return; } - description = PyBundle.message("INSP.unresolved.ref.$0.for.class.$1", refText, type.getName()); + description = PyLocalize.inspUnresolvedRef$0ForClass$1(refText, type.getName()); } markedQualified = true; } @@ -615,14 +613,14 @@ else if (isHasCustomMember(refName, type)) { return; } else { - description = PyBundle.message("INSP.cannot.find.$0.in.$1", refText, type.getName()); + description = PyLocalize.inspCannotFind$0In$1(refText, type.getName()); markedQualified = true; } } } } if (!markedQualified) { - description = PyBundle.message("INSP.unresolved.ref.$0", refText); + description = PyLocalize.inspUnresolvedRef$0(refText); // look in other imported modules for this whole name if (PythonImportUtils.isImportable(element)) { @@ -676,7 +674,7 @@ else if (severity == HighlightSeverity.ERROR) { } } - registerProblem(node, description, hl_type, null, rangeInElement, actions.toArray(new LocalQuickFix[actions.size()])); + registerProblem(node, description.get(), hl_type, null, rangeInElement, actions.toArray(new LocalQuickFix[actions.size()])); } private static void addInstallPackageAction(List actions, String packageName, Module module, Sdk sdk) { diff --git a/python-rest/src/main/java/com/jetbrains/python/rest/inspections/RestInspection.java b/python-rest/src/main/java/com/jetbrains/python/rest/inspections/RestInspection.java index 6299f98b..c56ffa5b 100644 --- a/python-rest/src/main/java/com/jetbrains/python/rest/inspections/RestInspection.java +++ b/python-rest/src/main/java/com/jetbrains/python/rest/inspections/RestInspection.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.rest.inspections; import com.jetbrains.rest.RestBundle; @@ -21,40 +20,38 @@ import consulo.language.editor.inspection.LocalInspectionTool; import consulo.language.editor.intention.SuppressIntentionAction; import consulo.language.psi.PsiElement; -import org.jetbrains.annotations.Nls; - +import consulo.localize.LocalizeValue; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; /** - * User : catherine + * @author catherine */ public abstract class RestInspection extends LocalInspectionTool implements CustomSuppressableInspectionTool { - @Nls - @Nonnull - @Override - public String getGroupDisplayName() { - return RestBundle.message("INSP.GROUP.rest"); - } - - @Nonnull - @Override - public String getShortName() { - return getClass().getSimpleName(); - } - - @Override - public boolean isEnabledByDefault() { - return true; - } - - @Override - public SuppressIntentionAction[] getSuppressActions(@Nullable PsiElement element) { - return null; - } - - @Override - public boolean isSuppressedFor(@Nonnull PsiElement element) { - return false; - } + @Nonnull + @Override + public LocalizeValue getGroupDisplayName() { + return LocalizeValue.localizeTODO(RestBundle.message("INSP.GROUP.rest")); + } + + @Nonnull + @Override + public String getShortName() { + return getClass().getSimpleName(); + } + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Override + public SuppressIntentionAction[] getSuppressActions(@Nullable PsiElement element) { + return null; + } + + @Override + public boolean isSuppressedFor(@Nonnull PsiElement element) { + return false; + } } diff --git a/python-rest/src/main/java/com/jetbrains/python/rest/inspections/RestRoleInspection.java b/python-rest/src/main/java/com/jetbrains/python/rest/inspections/RestRoleInspection.java index 1aaf9a14..dff0d5a2 100644 --- a/python-rest/src/main/java/com/jetbrains/python/rest/inspections/RestRoleInspection.java +++ b/python-rest/src/main/java/com/jetbrains/python/rest/inspections/RestRoleInspection.java @@ -35,6 +35,7 @@ import consulo.language.psi.PsiManager; import consulo.language.psi.util.PsiTreeUtil; import consulo.language.util.ModuleUtilCore; +import consulo.localize.LocalizeValue; import consulo.module.Module; import consulo.project.Project; import consulo.virtualFileSystem.LocalFileSystem; @@ -42,171 +43,146 @@ import org.jetbrains.annotations.Nls; import jakarta.annotation.Nonnull; + import java.util.HashSet; import java.util.List; import java.util.Set; /** - * User: catherine - *

- * Looks for using not defined roles + * Looks for using not defined roles. + * + * @author catherine */ @ExtensionImpl -public class RestRoleInspection extends RestInspection -{ - @Nonnull - @Override - public HighlightDisplayLevel getDefaultLevel() - { - return HighlightDisplayLevel.WARNING; - } - - @Nonnull - @Override - public InspectionToolState createStateProvider() - { - return new RestRoleInspectionState(); - } - - @Nls - @Nonnull - @Override - public String getDisplayName() - { - return RestBundle.message("INSP.role.not.defined"); - } - - @Override - public boolean isEnabledByDefault() - { - return false; - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, boolean isOnTheFly, LocalInspectionToolSession session, Object state) - { - RestRoleInspectionState inspectionState = (RestRoleInspectionState) state; - return new Visitor(holder, inspectionState.ignoredRoles); - } - - private class Visitor extends RestInspectionVisitor - { - private final Set myIgnoredRoles; - Set mySphinxRoles = new HashSet<>(); - - public Visitor(final ProblemsHolder holder, List ignoredRoles) - { - super(holder); - myIgnoredRoles = Set.copyOf(ignoredRoles); - Project project = holder.getProject(); - final Module module = ModuleUtilCore.findModuleForPsiElement(holder.getFile()); - if(module == null) - { - return; - } - String dir = ReSTService.getInstance(module).getWorkdir(); - if(!dir.isEmpty()) - { - fillSphinxRoles(dir, project); - } - } - - private void fillSphinxRoles(String dir, Project project) - { - VirtualFile config = LocalFileSystem.getInstance().findFileByPath((dir.endsWith("/") ? dir : dir + "/") + "conf.py"); - if(config == null) - { - return; - } - - PsiFile configFile = PsiManager.getInstance(project).findFile(config); - if(configFile instanceof PyFile) - { - PyFile file = (PyFile) configFile; - List functions = file.getTopLevelFunctions(); - for(PyFunction function : functions) - { - if(!"setup".equals(function.getName())) - { - continue; - } - PyStatementList stList = function.getStatementList(); - PyStatement[] statements = stList.getStatements(); - for(PyElement statement : statements) - { - if(statement instanceof PyExpressionStatement) - { - statement = ((PyExpressionStatement) statement).getExpression(); - } - if(statement instanceof PyCallExpression) - { - if(((PyCallExpression) statement).isCalleeText("add_role")) - { - PyExpression arg = ((PyCallExpression) statement).getArguments()[0]; - if(arg instanceof PyStringLiteralExpression) - { - mySphinxRoles.add(((PyStringLiteralExpression) arg).getStringValue()); - } - } - } - } - } - } - } - - @Override - public void visitRole(final RestRole node) - { - RestFile file = (RestFile) node.getContainingFile(); - - if(PsiTreeUtil.getParentOfType(node, RestDirectiveBlock.class) != null) - { - return; - } - final PsiElement sibling = node.getNextSibling(); - if(sibling == null || sibling.getNode().getElementType() != RestTokenTypes.INTERPRETED) - { - return; - } - if(RestUtil.PREDEFINED_ROLES.contains(node.getText()) || myIgnoredRoles.contains(node.getRoleName())) - { - return; - } - - if(RestUtil.SPHINX_ROLES.contains(node.getText()) || RestUtil.SPHINX_ROLES.contains(":py" + node.getText()) || mySphinxRoles.contains(node.getRoleName())) - { - return; - } - - Set definedRoles = new HashSet<>(); - - RestDirectiveBlock[] directives = PsiTreeUtil.getChildrenOfType(file, RestDirectiveBlock.class); - if(directives != null) - { - for(RestDirectiveBlock block : directives) - { - if(block.getDirectiveName().equals("role::")) - { - PsiElement role = block.getFirstChild().getNextSibling(); - if(role != null) - { - String roleName = role.getText().trim(); - int index = roleName.indexOf('('); - if(index != -1) - { - roleName = roleName.substring(0, index); - } - definedRoles.add(roleName); - } - } - } - } - if(definedRoles.contains(node.getRoleName())) - { - return; - } - registerProblem(node, "Not defined role '" + node.getRoleName() + "'", new AddIgnoredRoleFix(node.getRoleName())); - } - } +public class RestRoleInspection extends RestInspection { + @Nonnull + @Override + public HighlightDisplayLevel getDefaultLevel() { + return HighlightDisplayLevel.WARNING; + } + + @Nonnull + @Override + public InspectionToolState createStateProvider() { + return new RestRoleInspectionState(); + } + + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return LocalizeValue.localizeTODO(RestBundle.message("INSP.role.not.defined")); + } + + @Override + public boolean isEnabledByDefault() { + return false; + } + + @Nonnull + @Override + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + LocalInspectionToolSession session, + Object state + ) { + RestRoleInspectionState inspectionState = (RestRoleInspectionState) state; + return new Visitor(holder, inspectionState.ignoredRoles); + } + + private class Visitor extends RestInspectionVisitor { + private final Set myIgnoredRoles; + Set mySphinxRoles = new HashSet<>(); + + public Visitor(final ProblemsHolder holder, List ignoredRoles) { + super(holder); + myIgnoredRoles = Set.copyOf(ignoredRoles); + Project project = holder.getProject(); + final Module module = ModuleUtilCore.findModuleForPsiElement(holder.getFile()); + if (module == null) { + return; + } + String dir = ReSTService.getInstance(module).getWorkdir(); + if (!dir.isEmpty()) { + fillSphinxRoles(dir, project); + } + } + + private void fillSphinxRoles(String dir, Project project) { + VirtualFile config = LocalFileSystem.getInstance().findFileByPath((dir.endsWith("/") ? dir : dir + "/") + "conf.py"); + if (config == null) { + return; + } + + PsiFile configFile = PsiManager.getInstance(project).findFile(config); + if (configFile instanceof PyFile) { + PyFile file = (PyFile) configFile; + List functions = file.getTopLevelFunctions(); + for (PyFunction function : functions) { + if (!"setup".equals(function.getName())) { + continue; + } + PyStatementList stList = function.getStatementList(); + PyStatement[] statements = stList.getStatements(); + for (PyElement statement : statements) { + if (statement instanceof PyExpressionStatement) { + statement = ((PyExpressionStatement) statement).getExpression(); + } + if (statement instanceof PyCallExpression) { + if (((PyCallExpression) statement).isCalleeText("add_role")) { + PyExpression arg = ((PyCallExpression) statement).getArguments()[0]; + if (arg instanceof PyStringLiteralExpression) { + mySphinxRoles.add(((PyStringLiteralExpression) arg).getStringValue()); + } + } + } + } + } + } + } + + @Override + public void visitRole(final RestRole node) { + RestFile file = (RestFile) node.getContainingFile(); + + if (PsiTreeUtil.getParentOfType(node, RestDirectiveBlock.class) != null) { + return; + } + final PsiElement sibling = node.getNextSibling(); + if (sibling == null || sibling.getNode().getElementType() != RestTokenTypes.INTERPRETED) { + return; + } + if (RestUtil.PREDEFINED_ROLES.contains(node.getText()) || myIgnoredRoles.contains(node.getRoleName())) { + return; + } + + if (RestUtil.SPHINX_ROLES.contains(node.getText()) || RestUtil.SPHINX_ROLES.contains(":py" + node.getText()) || mySphinxRoles.contains( + node.getRoleName())) { + return; + } + + Set definedRoles = new HashSet<>(); + + RestDirectiveBlock[] directives = PsiTreeUtil.getChildrenOfType(file, RestDirectiveBlock.class); + if (directives != null) { + for (RestDirectiveBlock block : directives) { + if (block.getDirectiveName().equals("role::")) { + PsiElement role = block.getFirstChild().getNextSibling(); + if (role != null) { + String roleName = role.getText().trim(); + int index = roleName.indexOf('('); + if (index != -1) { + roleName = roleName.substring(0, index); + } + definedRoles.add(roleName); + } + } + } + } + if (definedRoles.contains(node.getRoleName())) { + return; + } + registerProblem(node, "Not defined role '" + node.getRoleName() + "'", new AddIgnoredRoleFix(node.getRoleName())); + } + } } From 108bd6a87ea7b2d1985d88f28273da1f0482eb31 Mon Sep 17 00:00:00 2001 From: UNV Date: Fri, 10 Oct 2025 20:59:18 +0300 Subject: [PATCH 2/2] Removing PyFlipComparisonIntention.getFamilyName. Refactoring. --- .../intentions/PyFlipComparisonIntention.java | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyFlipComparisonIntention.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyFlipComparisonIntention.java index 4a0809e4..a912e252 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyFlipComparisonIntention.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/intentions/PyFlipComparisonIntention.java @@ -30,7 +30,6 @@ import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; -import java.util.HashMap; import java.util.Map; /** @@ -38,22 +37,15 @@ * @since 2010-03-26 */ public class PyFlipComparisonIntention extends BaseIntentionAction { - private static final Map FLIPPED_OPERATORS = new HashMap(7); - - static { - FLIPPED_OPERATORS.put(PyTokenTypes.EQEQ, "=="); - FLIPPED_OPERATORS.put(PyTokenTypes.NE, "!="); - FLIPPED_OPERATORS.put(PyTokenTypes.NE_OLD, "<>"); - FLIPPED_OPERATORS.put(PyTokenTypes.GE, "<="); - FLIPPED_OPERATORS.put(PyTokenTypes.LE, ">="); - FLIPPED_OPERATORS.put(PyTokenTypes.GT, "<"); - FLIPPED_OPERATORS.put(PyTokenTypes.LT, ">"); - } - - @Nonnull - public String getFamilyName() { - return PyLocalize.intnFlipComparison().get(); - } + private static final Map OUR_FLIPPED_OPERATORS = Map.of( + PyTokenTypes.EQEQ, "==", + PyTokenTypes.NE, "!=", + PyTokenTypes.NE_OLD, "<>", + PyTokenTypes.GE, "<=", + PyTokenTypes.LE, ">=", + PyTokenTypes.GT, "<", + PyTokenTypes.LT, ">" + ); public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file) { if (!(file instanceof PyFile)) { @@ -64,9 +56,9 @@ public boolean isAvailable(@Nonnull Project project, Editor editor, PsiFile file PyBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PyBinaryExpression.class, false); while (binaryExpression != null) { PyElementType operator = binaryExpression.getOperator(); - if (FLIPPED_OPERATORS.containsKey(operator)) { + if (OUR_FLIPPED_OPERATORS.containsKey(operator)) { String operatorText = binaryExpression.getPsiOperator().getText(); - String flippedOperatorText = FLIPPED_OPERATORS.get(operator); + String flippedOperatorText = OUR_FLIPPED_OPERATORS.get(operator); if (flippedOperatorText.equals(operatorText)) { setText(PyLocalize.intnFlip$0(operatorText)); } @@ -84,10 +76,10 @@ public void invoke(@Nonnull Project project, Editor editor, PsiFile file) throws PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); PyBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(element, PyBinaryExpression.class, false); while (binaryExpression != null) { - if (FLIPPED_OPERATORS.containsKey(binaryExpression.getOperator())) { + if (OUR_FLIPPED_OPERATORS.containsKey(binaryExpression.getOperator())) { PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project); binaryExpression.replace(elementGenerator.createBinaryExpression( - FLIPPED_OPERATORS.get(binaryExpression.getOperator()), + OUR_FLIPPED_OPERATORS.get(binaryExpression.getOperator()), binaryExpression.getRightExpression(), binaryExpression.getLeftExpression() ));