diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyAbstractClassInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyAbstractClassInspection.java index 0f3b8ecc..3219fd43 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyAbstractClassInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyAbstractClassInspection.java @@ -15,12 +15,14 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.impl.codeInsight.override.PyOverrideImplementUtil; import com.jetbrains.python.impl.inspections.quickfix.PyImplementMethodsQuickFix; import com.jetbrains.python.impl.psi.PyUtil; -import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyExpression; +import com.jetbrains.python.psi.PyFunction; +import com.jetbrains.python.psi.PyReferenceExpression; import com.jetbrains.python.psi.types.PyClassLikeType; import com.jetbrains.python.psi.types.PyType; import consulo.annotation.component.ExtensionImpl; @@ -28,10 +30,11 @@ 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; + import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -39,78 +42,81 @@ import static com.jetbrains.python.impl.psi.PyUtil.as; /** - * User: ktisha + * @author ktisha */ @ExtensionImpl public class PyAbstractClassInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.abstract.class"); - } - - @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.inspNameAbstractClass(); } + @Nonnull @Override - public void visitPyClass(PyClass pyClass) { - if (isAbstract(pyClass)) { - return; - } - final Set toBeImplemented = new HashSet<>(); - final Collection functions = PyOverrideImplementUtil.getAllSuperFunctions(pyClass, myTypeEvalContext); - for (PyFunction method : functions) { - if (isAbstractMethodForClass(method, pyClass)) { - toBeImplemented.add(method); - } - } - final ASTNode nameNode = pyClass.getNameNode(); - if (!toBeImplemented.isEmpty() && nameNode != null) { - registerProblem(nameNode.getPsi(), - PyBundle.message("INSP.NAME.abstract.class.$0.must.implement", pyClass.getName()), - new PyImplementMethodsQuickFix(pyClass, toBeImplemented)); - } + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } - private boolean isAbstract(@Nonnull PyClass pyClass) { - final PyType metaClass = pyClass.getMetaClassType(myTypeEvalContext); - if (metaClass instanceof PyClassLikeType && PyNames.ABC_META_CLASS.equals(metaClass.getName())) { - return true; - } - if (metaClass == null) { - final PyExpression metaClassExpr = as(pyClass.getMetaClassExpression(), PyReferenceExpression.class); - if (metaClassExpr != null && PyNames.ABC_META_CLASS.equals(metaClassExpr.getName())) { - return true; + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); } - } - for (PyFunction method : pyClass.getMethods()) { - if (PyUtil.isDecoratedAsAbstract(method)) { - return true; + + @Override + public void visitPyClass(PyClass pyClass) { + if (isAbstract(pyClass)) { + return; + } + final Set toBeImplemented = new HashSet<>(); + final Collection functions = PyOverrideImplementUtil.getAllSuperFunctions(pyClass, myTypeEvalContext); + for (PyFunction method : functions) { + if (isAbstractMethodForClass(method, pyClass)) { + toBeImplemented.add(method); + } + } + final ASTNode nameNode = pyClass.getNameNode(); + if (!toBeImplemented.isEmpty() && nameNode != null) { + registerProblem( + nameNode.getPsi(), + PyLocalize.inspNameAbstractClass$0MustImplement(pyClass.getName()).get(), + new PyImplementMethodsQuickFix(pyClass, toBeImplemented) + ); + } } - } - return false; - } - private static boolean isAbstractMethodForClass(@Nonnull PyFunction method, @Nonnull PyClass cls) { - final String methodName = method.getName(); - if (methodName == null || - cls.findMethodByName(methodName, false, null) != null || - cls.findClassAttribute(methodName, false, null) != null) { - return false; - } - return PyUtil.isDecoratedAsAbstract(method) || PyOverrideImplementUtil.raisesNotImplementedError(method); + private boolean isAbstract(@Nonnull PyClass pyClass) { + final PyType metaClass = pyClass.getMetaClassType(myTypeEvalContext); + if (metaClass instanceof PyClassLikeType && PyNames.ABC_META_CLASS.equals(metaClass.getName())) { + return true; + } + if (metaClass == null) { + final PyExpression metaClassExpr = as(pyClass.getMetaClassExpression(), PyReferenceExpression.class); + if (metaClassExpr != null && PyNames.ABC_META_CLASS.equals(metaClassExpr.getName())) { + return true; + } + } + for (PyFunction method : pyClass.getMethods()) { + if (PyUtil.isDecoratedAsAbstract(method)) { + return true; + } + } + return false; + } + + private static boolean isAbstractMethodForClass(@Nonnull PyFunction method, @Nonnull PyClass cls) { + final String methodName = method.getName(); + if (methodName == null || + cls.findMethodByName(methodName, false, null) != null || + cls.findClassAttribute(methodName, false, null) != null) { + return false; + } + return PyUtil.isDecoratedAsAbstract(method) || PyOverrideImplementUtil.raisesNotImplementedError(method); + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyArgumentEqualDefaultInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyArgumentEqualDefaultInspection.java index f765fd75..8f569c14 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyArgumentEqualDefaultInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyArgumentEqualDefaultInspection.java @@ -15,10 +15,9 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.inspections.quickfix.RemoveArgumentEqualDefaultQuickFix; -import com.jetbrains.python.psi.*; import com.jetbrains.python.impl.psi.impl.PyBuiltinCache; +import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.types.PyClassType; import consulo.annotation.component.ExtensionImpl; import consulo.language.editor.inspection.LocalInspectionToolSession; @@ -26,158 +25,168 @@ import consulo.language.psi.PsiElement; import consulo.language.psi.PsiElementVisitor; 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 jakarta.annotation.Nullable; + import java.util.HashSet; import java.util.Map; import java.util.Set; /** - * User: catherine - *

* Inspection to detect situations, where argument passed to function * is equal to default parameter value * for instance, * dict().get(x, None) --> None is default value for second param in dict().get function + * + * @author catherine */ @ExtensionImpl public class PyArgumentEqualDefaultInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.argument.equal.default"); - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } - - @Override - public boolean isEnabledByDefault() { - return false; - } - - private static class Visitor extends PyInspectionVisitor { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameArgumentEqualDefault(); } + @Nonnull @Override - public void visitPyCallExpression(final PyCallExpression node) { - PyArgumentList list = node.getArgumentList(); - if (list == null) { - return; - } - PyCallable func = node.resolveCalleeFunction(getResolveContext()); - if (func != null && hasSpecialCasedDefaults(func, node)) { - return; - } - checkArguments(node, node.getArguments()); + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } @Override - public void visitPyDecoratorList(final PyDecoratorList node) { - PyDecorator[] decorators = node.getDecorators(); - - for (PyDecorator decorator : decorators) { - if (decorator.hasArgumentList()) { - PyExpression[] arguments = decorator.getArguments(); - checkArguments(decorator, arguments); - } - } + public boolean isEnabledByDefault() { + return false; } - private static boolean hasSpecialCasedDefaults(PyCallable callable, PsiElement anchor) { - final String name = callable.getName(); - final PyBuiltinCache cache = PyBuiltinCache.getInstance(anchor); - if ("getattr".equals(name) && cache.isBuiltin(callable)) { - return true; - } - else if ("get".equals(name) || "pop".equals(name)) { - final PyFunction method = callable.asMethod(); - final PyClassType dictType = cache.getDictType(); - if (method != null && dictType != null && method.getContainingClass() == dictType.getPyClass()) { - return true; + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); } - } - return false; - } - private void checkArguments(PyCallExpression callExpr, PyExpression[] arguments) { - final PyCallExpression.PyArgumentsMapping mapping = callExpr.mapArguments(getResolveContext()); - Set problemElements = new HashSet<>(); - for (Map.Entry e : mapping.getMappedParameters().entrySet()) { - PyExpression defaultValue = e.getValue().getDefaultValue(); - if (defaultValue != null) { - PyExpression key = e.getKey(); - if (key instanceof PyKeywordArgument && ((PyKeywordArgument)key).getValueExpression() != null) { - key = ((PyKeywordArgument)key).getValueExpression(); - } - if (isEqual(key, defaultValue)) { - problemElements.add(e.getKey()); - } + @Override + public void visitPyCallExpression(final PyCallExpression node) { + PyArgumentList list = node.getArgumentList(); + if (list == null) { + return; + } + PyCallable func = node.resolveCalleeFunction(getResolveContext()); + if (func != null && hasSpecialCasedDefaults(func, node)) { + return; + } + checkArguments(node, node.getArguments()); } - } - boolean canDelete = true; - for (int i = arguments.length - 1; i != -1; --i) { - if (problemElements.contains(arguments[i])) { - if (canDelete) { - registerProblem(arguments[i], - PyBundle.message("INSP.argument.equals.to.default"), - new RemoveArgumentEqualDefaultQuickFix(problemElements)); - } - else { - registerProblem(arguments[i], PyBundle.message("INSP.argument.equals.to.default")); - } + @Override + public void visitPyDecoratorList(final PyDecoratorList node) { + PyDecorator[] decorators = node.getDecorators(); + + for (PyDecorator decorator : decorators) { + if (decorator.hasArgumentList()) { + PyExpression[] arguments = decorator.getArguments(); + checkArguments(decorator, arguments); + } + } } - else if (!(arguments[i] instanceof PyKeywordArgument)) { - canDelete = false; - } - } - } - private boolean isEqual(PyExpression key, PyExpression defaultValue) { - if (isBothInstanceOf(key, defaultValue, PyNumericLiteralExpression.class) || - isBothInstanceOf(key, defaultValue, PyPrefixExpression.class) || isBothInstanceOf(key, defaultValue, PyBinaryExpression.class)) { - if (key.getText().equals(defaultValue.getText())) { - return true; + private static boolean hasSpecialCasedDefaults(PyCallable callable, PsiElement anchor) { + final String name = callable.getName(); + final PyBuiltinCache cache = PyBuiltinCache.getInstance(anchor); + if ("getattr".equals(name) && cache.isBuiltin(callable)) { + return true; + } + else if ("get".equals(name) || "pop".equals(name)) { + final PyFunction method = callable.asMethod(); + final PyClassType dictType = cache.getDictType(); + if (method != null && dictType != null && method.getContainingClass() == dictType.getPyClass()) { + return true; + } + } + return false; } - } - else if (key instanceof PyStringLiteralExpression && defaultValue instanceof PyStringLiteralExpression) { - if (((PyStringLiteralExpression)key).getStringValue().equals(((PyStringLiteralExpression)defaultValue).getStringValue())) { - return true; + + private void checkArguments(PyCallExpression callExpr, PyExpression[] arguments) { + final PyCallExpression.PyArgumentsMapping mapping = callExpr.mapArguments(getResolveContext()); + Set problemElements = new HashSet<>(); + for (Map.Entry e : mapping.getMappedParameters().entrySet()) { + PyExpression defaultValue = e.getValue().getDefaultValue(); + if (defaultValue != null) { + PyExpression key = e.getKey(); + if (key instanceof PyKeywordArgument && ((PyKeywordArgument) key).getValueExpression() != null) { + key = ((PyKeywordArgument) key).getValueExpression(); + } + if (isEqual(key, defaultValue)) { + problemElements.add(e.getKey()); + } + } + } + boolean canDelete = true; + for (int i = arguments.length - 1; i != -1; --i) { + if (problemElements.contains(arguments[i])) { + if (canDelete) { + registerProblem( + arguments[i], + PyLocalize.inspArgumentEqualsToDefault().get(), + new RemoveArgumentEqualDefaultQuickFix(problemElements) + ); + } + else { + registerProblem(arguments[i], PyLocalize.inspArgumentEqualsToDefault().get()); + } + } + else if (!(arguments[i] instanceof PyKeywordArgument)) { + canDelete = false; + } + } } - } - else { - PsiReference keyRef = - key instanceof PyReferenceExpression ? ((PyReferenceExpression)key).getReference(getResolveContext()) : key.getReference(); - PsiReference defRef = - defaultValue instanceof PyReferenceExpression ? ((PyReferenceExpression)defaultValue).getReference(getResolveContext()) : defaultValue - .getReference(); - if (keyRef != null && defRef != null) { - PsiElement keyResolve = keyRef.resolve(); - PsiElement defResolve = defRef.resolve(); - if (keyResolve != null && keyResolve.equals(defResolve)) { - return true; - } + + private boolean isEqual(PyExpression key, PyExpression defaultValue) { + if (isBothInstanceOf(key, defaultValue, PyNumericLiteralExpression.class) || + isBothInstanceOf(key, defaultValue, PyPrefixExpression.class) || isBothInstanceOf( + key, + defaultValue, + PyBinaryExpression.class + )) { + if (key.getText().equals(defaultValue.getText())) { + return true; + } + } + else if (key instanceof PyStringLiteralExpression && defaultValue instanceof PyStringLiteralExpression) { + if (((PyStringLiteralExpression) key).getStringValue() + .equals(((PyStringLiteralExpression) defaultValue).getStringValue())) { + return true; + } + } + else { + PsiReference keyRef = + key instanceof PyReferenceExpression ? ((PyReferenceExpression) key).getReference(getResolveContext()) : key.getReference(); + PsiReference defRef = + defaultValue instanceof PyReferenceExpression ? ((PyReferenceExpression) defaultValue).getReference(getResolveContext()) : defaultValue + .getReference(); + if (keyRef != null && defRef != null) { + PsiElement keyResolve = keyRef.resolve(); + PsiElement defResolve = defRef.resolve(); + if (keyResolve != null && keyResolve.equals(defResolve)) { + return true; + } + } + } + return false; } - } - return false; - } - private static boolean isBothInstanceOf(@Nonnull final PyExpression key, - @Nonnull final PyExpression defaultValue, - @Nonnull final Class clazz) { - return clazz.isInstance(key) && clazz.isInstance(defaultValue); + private static boolean isBothInstanceOf( + @Nonnull final PyExpression key, + @Nonnull final PyExpression defaultValue, + @Nonnull final Class clazz + ) { + return clazz.isInstance(key) && clazz.isInstance(defaultValue); + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyArgumentListInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyArgumentListInspection.java index b195423d..507467cb 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyArgumentListInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyArgumentListInspection.java @@ -16,17 +16,16 @@ package com.jetbrains.python.impl.inspections; import com.google.common.collect.Lists; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.impl.inspections.quickfix.PyRemoveArgumentQuickFix; import com.jetbrains.python.impl.inspections.quickfix.PyRenameArgumentQuickFix; import com.jetbrains.python.impl.psi.PyUtil; +import com.jetbrains.python.impl.psi.types.PyABCUtil; +import com.jetbrains.python.impl.psi.types.PyTypeChecker; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.resolve.PyResolveContext; -import com.jetbrains.python.impl.psi.types.PyABCUtil; import com.jetbrains.python.psi.types.PyType; -import com.jetbrains.python.impl.psi.types.PyTypeChecker; import com.jetbrains.python.psi.types.TypeEvalContext; import consulo.annotation.component.ExtensionImpl; import consulo.language.ast.ASTNode; @@ -37,10 +36,11 @@ import consulo.language.psi.PsiPolyVariantReference; 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; + import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -53,181 +53,198 @@ */ @ExtensionImpl public class PyArgumentListInspection extends PyInspection { - @Nls - @Nonnull - public String getDisplayName() { - return PyBundle.message("INSP.NAME.incorrect.call.arguments"); - } - - @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 { - - public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { - super(holder, session); + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameIncorrectCallArguments(); } + @Nonnull @Override - public void visitPyArgumentList(final PyArgumentList node) { - // analyze - inspectPyArgumentList(node, getHolder(), myTypeEvalContext); + public PsiElementVisitor buildVisitor( + @Nonnull final ProblemsHolder holder, + final boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } - @Override - public void visitPyDecoratorList(final PyDecoratorList node) { - PyDecorator[] decorators = node.getDecorators(); - for (PyDecorator deco : decorators) { - if (deco.hasArgumentList()) { - continue; + public static class Visitor extends PyInspectionVisitor { + public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { + super(holder, session); } - final PyCallExpression.PyMarkedCallee markedCallee = deco.resolveCallee(getResolveContext()); - if (markedCallee != null && !markedCallee.isImplicitlyResolved()) { - final PyCallable callable = markedCallee.getCallable(); - int firstParamOffset = markedCallee.getImplicitOffset(); - final List params = PyUtil.getParameters(callable, myTypeEvalContext); - final PyNamedParameter allegedFirstParam = - params.size() < firstParamOffset ? null : params.get(firstParamOffset - 1).getAsNamed(); - if (allegedFirstParam == null || allegedFirstParam.isKeywordContainer()) { - // no parameters left to pass function implicitly, or wrong param type - registerProblem(deco, - PyBundle.message("INSP.func.$0.lacks.first.arg", callable.getName())); // TODO: better names for anon lambdas - } - else { // possible unfilled params - for (int i = firstParamOffset; i < params.size(); i += 1) { - final PyParameter parameter = params.get(i); - if (parameter instanceof PySingleStarParameter) { - continue; - } - final PyNamedParameter par = parameter.getAsNamed(); - // param tuples, non-starred or non-default won't do - if (par == null || (!par.isKeywordContainer() && !par.isPositionalContainer() && !par.hasDefaultValue())) { - String parameterName = par != null ? par.getName() : "(...)"; - registerProblem(deco, PyBundle.message("INSP.parameter.$0.unfilled", parameterName)); - } - } - } + + @Override + public void visitPyArgumentList(final PyArgumentList node) { + // analyze + inspectPyArgumentList(node, getHolder(), myTypeEvalContext); } - // else: this case is handled by arglist visitor - } - } - } + @Override + public void visitPyDecoratorList(final PyDecoratorList node) { + PyDecorator[] decorators = node.getDecorators(); + for (PyDecorator deco : decorators) { + if (deco.hasArgumentList()) { + continue; + } + final PyCallExpression.PyMarkedCallee markedCallee = deco.resolveCallee(getResolveContext()); + if (markedCallee != null && !markedCallee.isImplicitlyResolved()) { + final PyCallable callable = markedCallee.getCallable(); + int firstParamOffset = markedCallee.getImplicitOffset(); + final List params = PyUtil.getParameters(callable, myTypeEvalContext); + final PyNamedParameter allegedFirstParam = + params.size() < firstParamOffset ? null : params.get(firstParamOffset - 1).getAsNamed(); + if (allegedFirstParam == null || allegedFirstParam.isKeywordContainer()) { + // no parameters left to pass function implicitly, or wrong param type + registerProblem( + deco, + PyLocalize.inspFunc$0LacksFirstArg(callable.getName()).get() + ); // TODO: better names for anon lambdas + } + else { // possible unfilled params + for (int i = firstParamOffset; i < params.size(); i += 1) { + final PyParameter parameter = params.get(i); + if (parameter instanceof PySingleStarParameter) { + continue; + } + final PyNamedParameter par = parameter.getAsNamed(); + // param tuples, non-starred or non-default won't do + if (par == null || (!par.isKeywordContainer() && !par.isPositionalContainer() && !par.hasDefaultValue())) { + String parameterName = par != null ? par.getName() : "(...)"; + registerProblem(deco, PyLocalize.inspParameter$0Unfilled(parameterName).get()); + } + } + } + } + // else: this case is handled by arglist visitor + } + } - public static void inspectPyArgumentList(PyArgumentList node, ProblemsHolder holder, final TypeEvalContext context, int implicitOffset) { - if (node.getParent() instanceof PyClass) { - return; // class Foo(object) is also an arg list - } - final PyCallExpression callExpr = node.getCallExpression(); - if (callExpr == null) { - return; } - final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context); - final PyCallExpression.PyArgumentsMapping mapping = callExpr.mapArguments(resolveContext, implicitOffset); - final PyCallExpression.PyMarkedCallee callee = mapping.getMarkedCallee(); - if (callee != null) { - final PyCallable callable = callee.getCallable(); - if (callable instanceof PyFunction) { - final PyFunction function = (PyFunction)callable; - - // Decorate functions may have different parameter lists. We don't match arguments with parameters of decorators yet - if (PyUtil.hasCustomDecorators(function) || decoratedClassInitCall(callExpr.getCallee(), function)) { - return; + + public static void inspectPyArgumentList( + PyArgumentList node, + ProblemsHolder holder, + final TypeEvalContext context, + int implicitOffset + ) { + if (node.getParent() instanceof PyClass) { + return; // class Foo(object) is also an arg list + } + final PyCallExpression callExpr = node.getCallExpression(); + if (callExpr == null) { + return; + } + final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context); + final PyCallExpression.PyArgumentsMapping mapping = callExpr.mapArguments(resolveContext, implicitOffset); + final PyCallExpression.PyMarkedCallee callee = mapping.getMarkedCallee(); + if (callee != null) { + final PyCallable callable = callee.getCallable(); + if (callable instanceof PyFunction) { + final PyFunction function = (PyFunction) callable; + + // Decorate functions may have different parameter lists. We don't match arguments with parameters of decorators yet + if (PyUtil.hasCustomDecorators(function) || decoratedClassInitCall(callExpr.getCallee(), function)) { + return; + } + } } - } + highlightParametersMismatch(node, holder, mapping); + highlightStarArgumentTypeMismatch(node, holder, context); } - highlightParametersMismatch(node, holder, mapping); - highlightStarArgumentTypeMismatch(node, holder, context); - } - public static void inspectPyArgumentList(PyArgumentList node, ProblemsHolder holder, final TypeEvalContext context) { - inspectPyArgumentList(node, holder, context, 0); - } + public static void inspectPyArgumentList(PyArgumentList node, ProblemsHolder holder, final TypeEvalContext context) { + inspectPyArgumentList(node, holder, context, 0); + } + + private static boolean decoratedClassInitCall(@Nullable PyExpression callee, @Nonnull PyFunction function) { + if (callee instanceof PyReferenceExpression && PyUtil.isInit(function)) { + final PsiPolyVariantReference classReference = ((PyReferenceExpression) callee).getReference(); - private static boolean decoratedClassInitCall(@Nullable PyExpression callee, @Nonnull PyFunction function) { - if (callee instanceof PyReferenceExpression && PyUtil.isInit(function)) { - final PsiPolyVariantReference classReference = ((PyReferenceExpression)callee).getReference(); + return Arrays.stream(classReference.multiResolve(false)) + .map(ResolveResult::getElement) + .anyMatch(element -> element instanceof PyClass && PyUtil.hasCustomDecorators((PyClass) element)); + } - return Arrays.stream(classReference.multiResolve(false)) - .map(ResolveResult::getElement) - .anyMatch(element -> element instanceof PyClass && PyUtil.hasCustomDecorators((PyClass)element)); + return false; } - return false; - } - - private static void highlightStarArgumentTypeMismatch(PyArgumentList node, ProblemsHolder holder, TypeEvalContext context) { - for (PyExpression arg : node.getArguments()) { - if (arg instanceof PyStarArgument) { - PyExpression content = PyUtil.peelArgument(PsiTreeUtil.findChildOfType(arg, PyExpression.class)); - if (content != null) { - PyType inside_type = context.getType(content); - if (inside_type != null && !PyTypeChecker.isUnknown(inside_type)) { - if (((PyStarArgument)arg).isKeyword()) { - if (!PyABCUtil.isSubtype(inside_type, PyNames.MAPPING, context)) { - holder.registerProblem(arg, PyBundle.message("INSP.expected.dict.got.$0", inside_type.getName())); - } + private static void highlightStarArgumentTypeMismatch(PyArgumentList node, ProblemsHolder holder, TypeEvalContext context) { + for (PyExpression arg : node.getArguments()) { + if (arg instanceof PyStarArgument) { + PyExpression content = PyUtil.peelArgument(PsiTreeUtil.findChildOfType(arg, PyExpression.class)); + if (content != null) { + PyType inside_type = context.getType(content); + if (inside_type != null && !PyTypeChecker.isUnknown(inside_type)) { + if (((PyStarArgument) arg).isKeyword()) { + if (!PyABCUtil.isSubtype(inside_type, PyNames.MAPPING, context)) { + holder.newProblem(PyLocalize.inspExpectedDictGot$0(inside_type.getName())) + .range(arg) + .create(); + } + } + else { // * arg + if (!PyABCUtil.isSubtype(inside_type, PyNames.ITERABLE, context)) { + holder.newProblem(PyLocalize.inspExpectedIterGot$0(inside_type.getName())) + .range(arg) + .create(); + } + } + } + } } - else { // * arg - if (!PyABCUtil.isSubtype(inside_type, PyNames.ITERABLE, context)) { - holder.registerProblem(arg, PyBundle.message("INSP.expected.iter.got.$0", inside_type.getName())); - } - } - } } - } } - } - - private static Set getDuplicateKeywordArguments(@Nonnull PyArgumentList node) { - final Set keywordArgumentNames = new HashSet<>(); - final Set results = new HashSet<>(); - for (PyExpression argument : node.getArguments()) { - if (argument instanceof PyKeywordArgument) { - final String keyword = ((PyKeywordArgument)argument).getKeyword(); - if (keywordArgumentNames.contains(keyword)) { - results.add(keyword); + + private static Set getDuplicateKeywordArguments(@Nonnull PyArgumentList node) { + final Set keywordArgumentNames = new HashSet<>(); + final Set results = new HashSet<>(); + for (PyExpression argument : node.getArguments()) { + if (argument instanceof PyKeywordArgument) { + final String keyword = ((PyKeywordArgument) argument).getKeyword(); + if (keywordArgumentNames.contains(keyword)) { + results.add(keyword); + } + keywordArgumentNames.add(keyword); + } } - keywordArgumentNames.add(keyword); - } + return results; } - return results; - } - - private static void highlightParametersMismatch(@Nonnull PyArgumentList node, - @Nonnull ProblemsHolder holder, - @Nonnull PyCallExpression.PyArgumentsMapping mapping) { - final Set duplicateKeywords = getDuplicateKeywordArguments(node); - for (PyExpression argument : mapping.getUnmappedArguments()) { - final List quickFixes = Lists.newArrayList(new PyRemoveArgumentQuickFix()); - if (argument instanceof PyKeywordArgument) { - if (duplicateKeywords.contains(((PyKeywordArgument)argument).getKeyword())) { - continue; + + private static void highlightParametersMismatch( + @Nonnull PyArgumentList node, + @Nonnull ProblemsHolder holder, + @Nonnull PyCallExpression.PyArgumentsMapping mapping + ) { + final Set duplicateKeywords = getDuplicateKeywordArguments(node); + for (PyExpression argument : mapping.getUnmappedArguments()) { + final List quickFixes = Lists.newArrayList(new PyRemoveArgumentQuickFix()); + if (argument instanceof PyKeywordArgument) { + if (duplicateKeywords.contains(((PyKeywordArgument) argument).getKeyword())) { + continue; + } + quickFixes.add(new PyRenameArgumentQuickFix()); + } + holder.newProblem(PyLocalize.inspUnexpectedArg()) + .range(argument) + .withFixes(quickFixes.toArray(new LocalQuickFix[quickFixes.size() - 1])) + .create(); } - quickFixes.add(new PyRenameArgumentQuickFix()); - } - holder.registerProblem(argument, - PyBundle.message("INSP.unexpected.arg"), - quickFixes.toArray(new LocalQuickFix[quickFixes.size() - 1])); - } - ASTNode our_node = node.getNode(); - if (our_node != null) { - ASTNode close_paren = our_node.findChildByType(PyTokenTypes.RPAR); - if (close_paren != null) { - for (PyParameter parameter : mapping.getUnmappedParameters()) { - final String name = parameter.getName(); - if (name != null) { - holder.registerProblem(close_paren.getPsi(), PyBundle.message("INSP.parameter.$0.unfilled", name)); - } + ASTNode our_node = node.getNode(); + if (our_node != null) { + ASTNode close_paren = our_node.findChildByType(PyTokenTypes.RPAR); + if (close_paren != null) { + for (PyParameter parameter : mapping.getUnmappedParameters()) { + String name = parameter.getName(); + if (name != null) { + holder.newProblem(PyLocalize.inspParameter$0Unfilled(name)) + .range(close_paren.getPsi()) + .create(); + } + } + } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyAttributeOutsideInitInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyAttributeOutsideInitInspection.java index d57f8cbc..2f257182 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyAttributeOutsideInitInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyAttributeOutsideInitInspection.java @@ -15,117 +15,122 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.impl.inspections.quickfix.PyMoveAttributeToInitQuickFix; +import com.jetbrains.python.impl.psi.impl.PyClassImpl; +import com.jetbrains.python.impl.testing.PythonUnitTestUtil; import com.jetbrains.python.psi.Property; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyTargetExpression; -import com.jetbrains.python.impl.psi.impl.PyClassImpl; import com.jetbrains.python.psi.types.TypeEvalContext; -import com.jetbrains.python.impl.testing.PythonUnitTestUtil; 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; + import java.util.HashMap; import java.util.List; import java.util.Map; /** - * User: ktisha - *

- * Inspection to detect situations, where instance attribute - * defined outside __init__ function + * Inspection to detect situations, where instance attribute defined outside __init__ function + * + * @author ktisha */ @ExtensionImpl public class PyAttributeOutsideInitInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.attribute.outside.init"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameAttributeOutsideInit(); + } - @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 { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); - } + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } - @Override - public void visitPyFunction(PyFunction node) { - final PyClass containingClass = node.getContainingClass(); - if (containingClass == null) { - return; - } - final String name = node.getName(); - if (name != null && name.startsWith("_")) { - return; - } - if (!isApplicable(containingClass, myTypeEvalContext)) { - return; - } + @Override + public void visitPyFunction(PyFunction node) { + final PyClass containingClass = node.getContainingClass(); + if (containingClass == null) { + return; + } + final String name = node.getName(); + if (name != null && name.startsWith("_")) { + return; + } + if (!isApplicable(containingClass, myTypeEvalContext)) { + return; + } - final PyFunction.Modifier modifier = node.getModifier(); - if (modifier != null) { - return; - } - final List classAttributes = containingClass.getClassAttributes(); + final PyFunction.Modifier modifier = node.getModifier(); + if (modifier != null) { + return; + } + final List classAttributes = containingClass.getClassAttributes(); - Map attributesInInit = new HashMap<>(); - for (PyTargetExpression classAttr : classAttributes) { - attributesInInit.put(classAttr.getName(), classAttr); - } + Map attributesInInit = new HashMap<>(); + for (PyTargetExpression classAttr : classAttributes) { + attributesInInit.put(classAttr.getName(), classAttr); + } - final PyFunction initMethod = containingClass.findMethodByName(PyNames.INIT, false, null); - if (initMethod != null) { - PyClassImpl.collectInstanceAttributes(initMethod, attributesInInit); - } - for (PyClass superClass : containingClass.getAncestorClasses(myTypeEvalContext)) { - final PyFunction superInit = superClass.findMethodByName(PyNames.INIT, false, null); - if (superInit != null) { - PyClassImpl.collectInstanceAttributes(superInit, attributesInInit); - } + final PyFunction initMethod = containingClass.findMethodByName(PyNames.INIT, false, null); + if (initMethod != null) { + PyClassImpl.collectInstanceAttributes(initMethod, attributesInInit); + } + for (PyClass superClass : containingClass.getAncestorClasses(myTypeEvalContext)) { + final PyFunction superInit = superClass.findMethodByName(PyNames.INIT, false, null); + if (superInit != null) { + PyClassImpl.collectInstanceAttributes(superInit, attributesInInit); + } - for (PyTargetExpression classAttr : superClass.getClassAttributes()) { - attributesInInit.put(classAttr.getName(), classAttr); - } - } + for (PyTargetExpression classAttr : superClass.getClassAttributes()) { + attributesInInit.put(classAttr.getName(), classAttr); + } + } - Map attributes = new HashMap<>(); - PyClassImpl.collectInstanceAttributes(node, attributes); + Map attributes = new HashMap<>(); + PyClassImpl.collectInstanceAttributes(node, attributes); - for (Map.Entry attribute : attributes.entrySet()) { - String attributeName = attribute.getKey(); - if (attributeName == null) { - continue; + for (Map.Entry attribute : attributes.entrySet()) { + String attributeName = attribute.getKey(); + if (attributeName == null) { + continue; + } + final Property property = containingClass.findProperty(attributeName, true, null); + if (!attributesInInit.containsKey(attributeName) && property == null) { + registerProblem( + attribute.getValue(), + PyLocalize.inspAttribute$0OutsideInit(attributeName).get(), + new PyMoveAttributeToInitQuickFix() + ); + } + } } - final Property property = containingClass.findProperty(attributeName, true, null); - if (!attributesInInit.containsKey(attributeName) && property == null) { - registerProblem(attribute.getValue(), - PyBundle.message("INSP.attribute.$0.outside.init", attributeName), - new PyMoveAttributeToInitQuickFix()); - } - } } - } - private static boolean isApplicable(@Nonnull PyClass containingClass, @Nonnull TypeEvalContext context) { - return !PythonUnitTestUtil.isUnitTestCaseClass(containingClass) && !containingClass.isSubclass("django.db.models.base.Model", context); - } + private static boolean isApplicable(@Nonnull PyClass containingClass, @Nonnull TypeEvalContext context) { + return !PythonUnitTestUtil.isUnitTestCaseClass(containingClass) && !containingClass.isSubclass( + "django.db.models.base.Model", + context + ); + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyAugmentAssignmentInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyAugmentAssignmentInspection.java index 47f7b748..7a05fbd0 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyAugmentAssignmentInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyAugmentAssignmentInspection.java @@ -13,107 +13,112 @@ * 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.AugmentedAssignmentQuickFix; -import com.jetbrains.python.psi.*; import com.jetbrains.python.impl.psi.impl.PyBuiltinCache; -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 consulo.annotation.component.ExtensionImpl; import consulo.language.ast.TokenSet; 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; /** - * User: catherine - *

* Inspection to detect assignments that can be replaced with augmented assignments. + * + * @author catherine */ @ExtensionImpl public class PyAugmentAssignmentInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.augment.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.inspNameAugmentAssignment(); } + @Nonnull @Override - public void visitPyAssignmentStatement(final PyAssignmentStatement node) { - final PyExpression value = node.getAssignedValue(); - if (value instanceof PyBinaryExpression) { - final PyExpression target = node.getLeftHandSideExpression(); - final PyBinaryExpression expression = (PyBinaryExpression)value; - PyExpression leftExpression = expression.getLeftExpression(); - PyExpression rightExpression = expression.getRightExpression(); + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); + } - if (rightExpression instanceof PyParenthesizedExpression) { - rightExpression = ((PyParenthesizedExpression)rightExpression).getContainedExpression(); - } - if (rightExpression == null || target == null) { - return; - } - boolean changedParts = false; - final String targetText = target.getText(); - final String rightText = rightExpression.getText(); - if (rightText.equals(targetText)) { - final PyExpression tmp = rightExpression; - rightExpression = leftExpression; - leftExpression = tmp; - changedParts = true; + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); } - final PyElementType op = expression.getOperator(); - final TokenSet operations = TokenSet.create(PyTokenTypes.PLUS, PyTokenTypes.MINUS, PyTokenTypes.MULT, - PyTokenTypes.FLOORDIV, PyTokenTypes.DIV, PyTokenTypes.PERC, PyTokenTypes.AND, - PyTokenTypes.OR, PyTokenTypes.XOR, PyTokenTypes.LTLT, PyTokenTypes.GTGT, - PyTokenTypes.EXP); - final TokenSet commutativeOperations = TokenSet.create(PyTokenTypes.PLUS, PyTokenTypes.MULT, PyTokenTypes.OR, PyTokenTypes.AND); - if ((operations.contains(op) && !changedParts) || (changedParts && commutativeOperations.contains(op))) { - if (leftExpression.getText() - .equals(targetText) && (leftExpression instanceof PyReferenceExpression || leftExpression instanceof PySubscriptionExpression)) { - final PyType type = myTypeEvalContext.getType(rightExpression); - if (type != null && !PyTypeChecker.isUnknown(type)) { - final PyBuiltinCache cache = PyBuiltinCache.getInstance(rightExpression); - final LanguageLevel languageLevel = LanguageLevel.forElement(rightExpression); - if (isNumeric(type, cache) || (isString(type, cache, languageLevel) && !changedParts)) { - registerProblem(node, "Assignment can be replaced with augmented assignment", new AugmentedAssignmentQuickFix()); - } + @Override + public void visitPyAssignmentStatement(final PyAssignmentStatement node) { + final PyExpression value = node.getAssignedValue(); + if (value instanceof PyBinaryExpression) { + final PyExpression target = node.getLeftHandSideExpression(); + final PyBinaryExpression expression = (PyBinaryExpression) value; + PyExpression leftExpression = expression.getLeftExpression(); + PyExpression rightExpression = expression.getRightExpression(); + + if (rightExpression instanceof PyParenthesizedExpression) { + rightExpression = ((PyParenthesizedExpression) rightExpression).getContainedExpression(); + } + if (rightExpression == null || target == null) { + return; + } + boolean changedParts = false; + final String targetText = target.getText(); + final String rightText = rightExpression.getText(); + if (rightText.equals(targetText)) { + final PyExpression tmp = rightExpression; + rightExpression = leftExpression; + leftExpression = tmp; + changedParts = true; + } + + final PyElementType op = expression.getOperator(); + final TokenSet operations = TokenSet.create(PyTokenTypes.PLUS, PyTokenTypes.MINUS, PyTokenTypes.MULT, + PyTokenTypes.FLOORDIV, PyTokenTypes.DIV, PyTokenTypes.PERC, PyTokenTypes.AND, + PyTokenTypes.OR, PyTokenTypes.XOR, PyTokenTypes.LTLT, PyTokenTypes.GTGT, + PyTokenTypes.EXP + ); + final TokenSet commutativeOperations = + TokenSet.create(PyTokenTypes.PLUS, PyTokenTypes.MULT, PyTokenTypes.OR, PyTokenTypes.AND); + if ((operations.contains(op) && !changedParts) || (changedParts && commutativeOperations.contains(op))) { + if (leftExpression.getText() + .equals(targetText) && (leftExpression instanceof PyReferenceExpression || leftExpression instanceof PySubscriptionExpression)) { + final PyType type = myTypeEvalContext.getType(rightExpression); + if (type != null && !PyTypeChecker.isUnknown(type)) { + final PyBuiltinCache cache = PyBuiltinCache.getInstance(rightExpression); + final LanguageLevel languageLevel = LanguageLevel.forElement(rightExpression); + if (isNumeric(type, cache) || (isString(type, cache, languageLevel) && !changedParts)) { + registerProblem( + node, + "Assignment can be replaced with augmented assignment", + new AugmentedAssignmentQuickFix() + ); + } + } + } + } } - } } - } - } - private boolean isString(PyType type, PyBuiltinCache cache, LanguageLevel level) { - return PyTypeChecker.match(cache.getStringType(level), type, myTypeEvalContext); - } + private boolean isString(PyType type, PyBuiltinCache cache, LanguageLevel level) { + return PyTypeChecker.match(cache.getStringType(level), type, myTypeEvalContext); + } - private boolean isNumeric(PyType type, PyBuiltinCache cache) { - return PyTypeChecker.match(cache.getComplexType(), type, myTypeEvalContext); + private boolean isNumeric(PyType type, PyBuiltinCache cache) { + return PyTypeChecker.match(cache.getComplexType(), type, myTypeEvalContext); + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyBaseDocstringInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyBaseDocstringInspection.java index 293e1aee..9713a0dd 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyBaseDocstringInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyBaseDocstringInspection.java @@ -15,11 +15,10 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.psi.*; import com.jetbrains.python.impl.testing.PythonUnitTestUtil; +import com.jetbrains.python.psi.*; import consulo.language.editor.inspection.LocalInspectionToolSession; import consulo.language.editor.inspection.ProblemsHolder; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -27,51 +26,56 @@ * @author Mikhail Golubev */ public abstract class PyBaseDocstringInspection extends PyInspection { - @Nonnull - @Override - public abstract Visitor buildVisitor(@Nonnull ProblemsHolder holder, boolean isOnTheFly, @Nonnull LocalInspectionToolSession session, Object state); + @Nonnull + @Override + public abstract Visitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ); - protected static abstract class Visitor extends PyInspectionVisitor { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); - } + protected static abstract class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } - @Override - public final void visitPyFile(@Nonnull PyFile node) { - checkDocString(node); - } + @Override + public final void visitPyFile(@Nonnull PyFile node) { + checkDocString(node); + } - @Override - public final void visitPyFunction(@Nonnull PyFunction node) { - if (PythonUnitTestUtil.isUnitTestCaseFunction(node)) { - return; - } - final PyClass containingClass = node.getContainingClass(); - if (containingClass != null && PythonUnitTestUtil.isUnitTestCaseClass(containingClass)) { - return; - } - final Property property = node.getProperty(); - if (property != null && (node == property.getSetter().valueOrNull() || node == property.getDeleter().valueOrNull())) { - return; - } - final String name = node.getName(); - if (name != null && !name.startsWith("_")) { - checkDocString(node); - } - } + @Override + public final void visitPyFunction(@Nonnull PyFunction node) { + if (PythonUnitTestUtil.isUnitTestCaseFunction(node)) { + return; + } + final PyClass containingClass = node.getContainingClass(); + if (containingClass != null && PythonUnitTestUtil.isUnitTestCaseClass(containingClass)) { + return; + } + final Property property = node.getProperty(); + if (property != null && (node == property.getSetter().valueOrNull() || node == property.getDeleter().valueOrNull())) { + return; + } + final String name = node.getName(); + if (name != null && !name.startsWith("_")) { + checkDocString(node); + } + } - @Override - public final void visitPyClass(@Nonnull PyClass node) { - if (PythonUnitTestUtil.isUnitTestCaseClass(node)) { - return; - } - final String name = node.getName(); - if (name == null || name.startsWith("_")) { - return; - } - checkDocString(node); - } + @Override + public final void visitPyClass(@Nonnull PyClass node) { + if (PythonUnitTestUtil.isUnitTestCaseClass(node)) { + return; + } + final String name = node.getName(); + if (name == null || name.startsWith("_")) { + return; + } + checkDocString(node); + } - protected abstract void checkDocString(@Nonnull PyDocStringOwner node); - } + protected abstract void checkDocString(@Nonnull PyDocStringOwner node); + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyBroadExceptionInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyBroadExceptionInspection.java index 4cafebd1..e06068f1 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyBroadExceptionInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyBroadExceptionInspection.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.types.PyClassType; import com.jetbrains.python.psi.types.PyType; @@ -26,9 +25,9 @@ import consulo.language.psi.PsiElement; import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiReference; +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; @@ -40,91 +39,92 @@ */ @ExtensionImpl public class PyBroadExceptionInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.too.broad.exception.clauses"); - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameTooBroadExceptionClauses(); + } - public static boolean equalsException(@Nonnull PyClass cls, @Nonnull TypeEvalContext context) { - final PyType type = context.getType(cls); - return ("Exception".equals(cls.getName()) || "BaseException".equals(cls.getName())) && type != null && type.isBuiltin(); - } + @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); + public static boolean equalsException(@Nonnull PyClass cls, @Nonnull TypeEvalContext context) { + final PyType type = context.getType(cls); + return ("Exception".equals(cls.getName()) || "BaseException".equals(cls.getName())) && type != null && type.isBuiltin(); } - @Override - public void visitPyExceptBlock(final PyExceptPart node) { - PyExpression exceptClass = node.getExceptClass(); - if (reRaised(node)) { - return; - } - if (exceptClass == null) { - registerProblem(node.getFirstChild(), "Too broad exception clause"); - } - if (exceptClass != null) { - final PyType type = myTypeEvalContext.getType(exceptClass); - if (type instanceof PyClassType) { - final PyClass cls = ((PyClassType)type).getPyClass(); - final PyExpression target = node.getTarget(); - if (equalsException(cls, myTypeEvalContext) && (target == null || !isExceptionUsed(node, target.getText()))) { - registerProblem(exceptClass, "Too broad exception clause"); - } + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); } - } - } - private static boolean reRaised(PyExceptPart node) { - final PyStatementList statementList = node.getStatementList(); - if (statementList != null) { - for (PyStatement st : statementList.getStatements()) { - if (st instanceof PyRaiseStatement) { - return true; - } + @Override + public void visitPyExceptBlock(final PyExceptPart node) { + PyExpression exceptClass = node.getExceptClass(); + if (reRaised(node)) { + return; + } + if (exceptClass == null) { + registerProblem(node.getFirstChild(), "Too broad exception clause"); + } + if (exceptClass != null) { + final PyType type = myTypeEvalContext.getType(exceptClass); + if (type instanceof PyClassType) { + final PyClass cls = ((PyClassType) type).getPyClass(); + final PyExpression target = node.getTarget(); + if (equalsException(cls, myTypeEvalContext) && (target == null || !isExceptionUsed(node, target.getText()))) { + registerProblem(exceptClass, "Too broad exception clause"); + } + } + } } - } - return false; - } - private static boolean isExceptionUsed(PyExceptPart node, String text) { - Stack stack = new Stack<>(); - PyStatementList statementList = node.getStatementList(); - if (statementList != null) { - for (PyStatement st : statementList.getStatements()) { - stack.push(st); - while (!stack.isEmpty()) { - PsiElement e = stack.pop(); - if (e instanceof PyReferenceExpression) { - PsiReference reference = e.getReference(); - if (reference != null) { - PsiElement resolved = reference.resolve(); - if (resolved != null) { - if (resolved.getText().equals(text)) { - return true; - } + private static boolean reRaised(PyExceptPart node) { + final PyStatementList statementList = node.getStatementList(); + if (statementList != null) { + for (PyStatement st : statementList.getStatements()) { + if (st instanceof PyRaiseStatement) { + return true; + } } - } } - for (PsiElement psiElement : e.getChildren()) { - stack.push(psiElement); + return false; + } + + private static boolean isExceptionUsed(PyExceptPart node, String text) { + Stack stack = new Stack<>(); + PyStatementList statementList = node.getStatementList(); + if (statementList != null) { + for (PyStatement st : statementList.getStatements()) { + stack.push(st); + while (!stack.isEmpty()) { + PsiElement e = stack.pop(); + if (e instanceof PyReferenceExpression) { + PsiReference reference = e.getReference(); + if (reference != null) { + PsiElement resolved = reference.resolve(); + if (resolved != null) { + if (resolved.getText().equals(text)) { + return true; + } + } + } + } + for (PsiElement psiElement : e.getChildren()) { + stack.push(psiElement); + } + } + } } - } + return false; } - } - return false; } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyByteLiteralInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyByteLiteralInspection.java index 8522163e..42ce15c1 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyByteLiteralInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyByteLiteralInspection.java @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.inspections; +import com.jetbrains.python.PythonFileType; +import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.psi.PyStringLiteralExpression; import consulo.annotation.component.ExtensionImpl; import consulo.language.editor.inspection.LocalInspectionToolSession; import consulo.language.editor.inspection.ProblemsHolder; @@ -23,11 +25,8 @@ import consulo.language.psi.PsiElement; import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiFile; -import com.jetbrains.python.impl.PyBundle; -import com.jetbrains.python.PythonFileType; -import com.jetbrains.python.psi.PyFile; -import com.jetbrains.python.psi.PyStringLiteralExpression; -import org.jetbrains.annotations.Nls; +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -42,73 +41,80 @@ */ @ExtensionImpl public class PyByteLiteralInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.byte.literal"); - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } - - private class Visitor extends PyInspectionVisitor { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameByteLiteral(); } + @Nonnull @Override - public void visitComment(PsiComment node) { - checkString(node, node.getText()); + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } - - private void checkString(PsiElement node, String value) { - PsiFile file = node.getContainingFile(); // can't cache this in the instance, alas - if (file == null) return; - boolean default_bytes = false; - if (file instanceof PyFile) { - PyFile pyfile = (PyFile)file; - default_bytes = (!UNICODE_LITERALS.requiredAt(pyfile.getLanguageLevel()) && - !pyfile.hasImportFromFuture(UNICODE_LITERALS) - ); - } - final String charsetString = PythonFileType.getCharsetFromEncodingDeclaration(file.getText()); - try { - if (charsetString != null && !Charset.forName(charsetString).equals(Charset.forName("US-ASCII"))) - default_bytes = false; - } catch (UnsupportedCharsetException exception) {} - catch (IllegalCharsetNameException e) {} - - boolean hasNonAscii = false; + private class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } - int length = value.length(); - char c = 0; - for (int i = 0; i < length; ++i) { - c = value.charAt(i); - if (((int) c) > 255) { - hasNonAscii = true; - break; + @Override + public void visitComment(PsiComment node) { + checkString(node, node.getText()); } - } - char first_char = Character.toLowerCase(node.getText().charAt(0)); - boolean isByte = first_char == 'b' || (default_bytes && first_char != 'u'); + private void checkString(PsiElement node, String value) { + PsiFile file = node.getContainingFile(); // can't cache this in the instance, alas + if (file == null) { + return; + } + boolean default_bytes = false; + if (file instanceof PyFile) { + PyFile pyfile = (PyFile) file; + default_bytes = (!UNICODE_LITERALS.requiredAt(pyfile.getLanguageLevel()) && + !pyfile.hasImportFromFuture(UNICODE_LITERALS) + ); + } - if (hasNonAscii && isByte) { - registerProblem(node, "Byte literal contains characters > 255"); - } - } + final String charsetString = PythonFileType.getCharsetFromEncodingDeclaration(file.getText()); + try { + if (charsetString != null && !Charset.forName(charsetString).equals(Charset.forName("US-ASCII"))) { + default_bytes = false; + } + } + catch (UnsupportedCharsetException exception) { + } + catch (IllegalCharsetNameException e) { + } - @Override - public void visitPyStringLiteralExpression(PyStringLiteralExpression node) { - checkString(node, node.getStringValue()); + boolean hasNonAscii = false; + + int length = value.length(); + char c = 0; + for (int i = 0; i < length; ++i) { + c = value.charAt(i); + if (((int) c) > 255) { + hasNonAscii = true; + break; + } + } + + char first_char = Character.toLowerCase(node.getText().charAt(0)); + boolean isByte = first_char == 'b' || (default_bytes && first_char != 'u'); + + if (hasNonAscii && isByte) { + registerProblem(node, "Byte literal contains characters > 255"); + } + } + + @Override + public void visitPyStringLiteralExpression(PyStringLiteralExpression node) { + checkString(node, node.getStringValue()); + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyCallByClassInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyCallByClassInspection.java index c4fff4fa..be0d0723 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyCallByClassInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyCallByClassInspection.java @@ -15,7 +15,6 @@ */ 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.psi.types.PyClassType; @@ -26,9 +25,10 @@ import consulo.language.editor.rawHighlight.HighlightDisplayLevel; 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 java.util.List; import java.util.Map; @@ -36,119 +36,130 @@ import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD; /** - * Checks for for calls like X.method(y,...), where y is not an instance of X. - *
- * Not marked are cases of inheritance calls in old-style classes, like:

+ * Checks for for calls like {@code X.method(y, ...)}, where y is not an instance of X.
+ *
+ * 

Not marked are cases of inheritance calls in old-style classes, like:

  * class B(A):
  *   def foo(self):
  *     A.foo(self)
- * 
- *
- * User: dcheryasov - * Date: Sep 22, 2010 12:21:44 PM + *

+ * + * @author dcheryasov + * @since 2010-09-22 */ @ExtensionImpl public class PyCallByClassInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.different.class.call"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameDifferentClassCall(); + } - @Override - public boolean isEnabledByDefault() { - return true; - } + @Override + public boolean isEnabledByDefault() { + return true; + } - @Nonnull - @Override - public HighlightDisplayLevel getDefaultLevel() { - return HighlightDisplayLevel.WEAK_WARNING; - } + @Nonnull + @Override + public HighlightDisplayLevel getDefaultLevel() { + return HighlightDisplayLevel.WEAK_WARNING; + } - @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); + } - public static class Visitor extends PyInspectionVisitor { + public 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 call) { - PyExpression callee = call.getCallee(); - if (callee instanceof PyQualifiedExpression) { - PyExpression qualifier = ((PyQualifiedExpression)callee).getQualifier(); - if (qualifier != null) { - PyType qual_type = myTypeEvalContext.getType(qualifier); - if (qual_type instanceof PyClassType) { - final PyClassType qual_class_type = (PyClassType)qual_type; - if (qual_class_type.isDefinition()) { - PyClass qual_class = qual_class_type.getPyClass(); - final PyArgumentList arglist = call.getArgumentList(); - if (arglist != null) { - final PyCallExpression.PyArgumentsMapping mapping = call.mapArguments(getResolveContext()); - final PyCallExpression.PyMarkedCallee markedCallee = mapping.getMarkedCallee(); - if (markedCallee != null && markedCallee.getModifier() != STATICMETHOD) { - final List params = PyUtil.getParameters(markedCallee.getCallable(), myTypeEvalContext); - if (params.size() > 0 && params.get(0) instanceof PyNamedParameter) { - PyNamedParameter first_param = (PyNamedParameter)params.get(0); - for (Map.Entry entry : mapping.getMappedParameters().entrySet()) { - // we ignore *arg and **arg which we cannot analyze - if (entry.getValue() == first_param) { - PyExpression first_arg = entry.getKey(); - assert first_arg != null; - PyType first_arg_type = myTypeEvalContext.getType(first_arg); - if (first_arg_type instanceof PyClassType) { - final PyClassType first_arg_class_type = (PyClassType)first_arg_type; - if (first_arg_class_type.isDefinition() && markedCallee.getModifier() != CLASSMETHOD) { - registerProblem(first_arg, PyBundle.message("INSP.instance.of.$0.excpected", qual_class.getQualifiedName())); - } - PyClass first_arg_class = first_arg_class_type.getPyClass(); - if (first_arg_class != qual_class) { - // delegating to a parent is fine - if (markedCallee.getCallable() instanceof PyFunction) { - PyCallable callable = PsiTreeUtil.getParentOfType(call, PyCallable.class); - if (callable != null) { - PyFunction method = callable.asMethod(); - if (method != null) { - PyClass calling_class = method.getContainingClass(); - assert calling_class != null; // it's a method - if (first_arg_class.isSubclass(qual_class, myTypeEvalContext) && calling_class.isSubclass(qual_class, - myTypeEvalContext)) { - break; - // TODO: might propose to switch to super() here - } + @Override + public void visitPyCallExpression(PyCallExpression call) { + PyExpression callee = call.getCallee(); + if (callee instanceof PyQualifiedExpression) { + PyExpression qualifier = ((PyQualifiedExpression) callee).getQualifier(); + if (qualifier != null) { + PyType qual_type = myTypeEvalContext.getType(qualifier); + if (qual_type instanceof PyClassType) { + final PyClassType qual_class_type = (PyClassType) qual_type; + if (qual_class_type.isDefinition()) { + PyClass qual_class = qual_class_type.getPyClass(); + final PyArgumentList arglist = call.getArgumentList(); + if (arglist != null) { + final PyCallExpression.PyArgumentsMapping mapping = call.mapArguments(getResolveContext()); + final PyCallExpression.PyMarkedCallee markedCallee = mapping.getMarkedCallee(); + if (markedCallee != null && markedCallee.getModifier() != STATICMETHOD) { + final List params = PyUtil.getParameters(markedCallee.getCallable(), myTypeEvalContext); + if (params.size() > 0 && params.get(0) instanceof PyNamedParameter) { + PyNamedParameter first_param = (PyNamedParameter) params.get(0); + for (Map.Entry entry : mapping.getMappedParameters().entrySet()) { + // we ignore *arg and **arg which we cannot analyze + if (entry.getValue() == first_param) { + PyExpression first_arg = entry.getKey(); + assert first_arg != null; + PyType first_arg_type = myTypeEvalContext.getType(first_arg); + if (first_arg_type instanceof PyClassType) { + final PyClassType first_arg_class_type = (PyClassType) first_arg_type; + if (first_arg_class_type.isDefinition() && markedCallee.getModifier() != CLASSMETHOD) { + registerProblem( + first_arg, + PyLocalize.inspInstanceOf$0Excpected(qual_class.getQualifiedName()).get() + ); + } + PyClass first_arg_class = first_arg_class_type.getPyClass(); + if (first_arg_class != qual_class) { + // delegating to a parent is fine + if (markedCallee.getCallable() instanceof PyFunction) { + PyCallable callable = PsiTreeUtil.getParentOfType(call, PyCallable.class); + if (callable != null) { + PyFunction method = callable.asMethod(); + if (method != null) { + PyClass calling_class = method.getContainingClass(); + assert calling_class != null; // it's a method + if (first_arg_class.isSubclass( + qual_class, + myTypeEvalContext + ) && calling_class.isSubclass( + qual_class, + myTypeEvalContext + )) { + break; + // TODO: might propose to switch to super() here + } + } + } + } + // otherwise, it's not + registerProblem( + first_arg, + PyLocalize.inspPassing$0InsteadOf$1( + first_arg_class.getQualifiedName(), + qual_class.getQualifiedName() + ).get() + ); + } + } + break; // once we found the first parameter, we don't need the rest + } + } + } } - } } - // otherwise, it's not - registerProblem(first_arg, - PyBundle.message("INSP.passing.$0.instead.of.$1", - first_arg_class.getQualifiedName(), - qual_class.getQualifiedName - ())); - } } - break; // once we found the first parameter, we don't need the rest - } } - } } - } } - } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyCallingNonCallableInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyCallingNonCallableInspection.java index f6c3200e..91cdbe19 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyCallingNonCallableInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyCallingNonCallableInspection.java @@ -13,21 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.inspections; import com.jetbrains.python.PyNames; +import com.jetbrains.python.impl.psi.types.PyTypeChecker; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.types.PyClassType; import com.jetbrains.python.psi.types.PyType; -import com.jetbrains.python.impl.psi.types.PyTypeChecker; 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.PsiElementVisitor; -import org.jetbrains.annotations.Nls; - +import consulo.localize.LocalizeValue; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -36,70 +34,71 @@ */ @ExtensionImpl public class PyCallingNonCallableInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return "Trying to call a non-callable object"; - } - - @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 LocalizeValue.localizeTODO("Trying to call a non-callable object"); } + @Nonnull @Override - public void visitPyCallExpression(PyCallExpression node) { - super.visitPyCallExpression(node); - checkCallable(node, node.getCallee(), null); + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } - @Override - public void visitPyDecoratorList(PyDecoratorList node) { - super.visitPyDecoratorList(node); - for (PyDecorator decorator : node.getDecorators()) { - final PyExpression callee = decorator.getCallee(); - checkCallable(decorator, callee, null); - if (decorator.hasArgumentList()) { - checkCallable(decorator, decorator, null); + public static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); } - } - } - private void checkCallable(@Nonnull PyElement node, @Nullable PyExpression callee, @Nullable PyType type) { - final Boolean callable = callee != null ? isCallable(callee, myTypeEvalContext) : PyTypeChecker.isCallable(type); - if (callable == null) { - return; - } - if (!callable) { - final PyType calleeType = callee != null ? myTypeEvalContext.getType(callee) : type; - if (calleeType instanceof PyClassType) { - registerProblem(node, String.format("'%s' object is not callable", calleeType.getName())); + @Override + public void visitPyCallExpression(PyCallExpression node) { + super.visitPyCallExpression(node); + checkCallable(node, node.getCallee(), null); } - else if (callee != null) { - registerProblem(node, String.format("'%s' is not callable", callee.getName())); + + @Override + public void visitPyDecoratorList(PyDecoratorList node) { + super.visitPyDecoratorList(node); + for (PyDecorator decorator : node.getDecorators()) { + final PyExpression callee = decorator.getCallee(); + checkCallable(decorator, callee, null); + if (decorator.hasArgumentList()) { + checkCallable(decorator, decorator, null); + } + } } - else { - registerProblem(node, "Expression is not callable"); + + private void checkCallable(@Nonnull PyElement node, @Nullable PyExpression callee, @Nullable PyType type) { + final Boolean callable = callee != null ? isCallable(callee, myTypeEvalContext) : PyTypeChecker.isCallable(type); + if (callable == null) { + return; + } + if (!callable) { + final PyType calleeType = callee != null ? myTypeEvalContext.getType(callee) : type; + if (calleeType instanceof PyClassType) { + registerProblem(node, String.format("'%s' object is not callable", calleeType.getName())); + } + else if (callee != null) { + registerProblem(node, String.format("'%s' is not callable", callee.getName())); + } + else { + registerProblem(node, "Expression is not callable"); + } + } } - } } - } - @Nullable - private static Boolean isCallable(@Nonnull PyExpression element, @Nonnull TypeEvalContext context) { - if (element instanceof PyQualifiedExpression && PyNames.CLASS.equals(element.getName())) { - return true; + @Nullable + private static Boolean isCallable(@Nonnull PyExpression element, @Nonnull TypeEvalContext context) { + if (element instanceof PyQualifiedExpression && PyNames.CLASS.equals(element.getName())) { + return true; + } + return PyTypeChecker.isCallable(context.getType(element)); } - return PyTypeChecker.isCallable(context.getType(element)); - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyChainedComparisonsInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyChainedComparisonsInspection.java index 56cefa48..d812f266 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyChainedComparisonsInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyChainedComparisonsInspection.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.ChainedComparisonsQuickFix; import com.jetbrains.python.psi.PyBinaryExpression; @@ -26,175 +24,186 @@ 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; /** - * User: catherine - *

* Inspection to detect chained comparisons which can be simplified * For instance, a < b and b < c --> a < b < c + * + * @author catherine */ @ExtensionImpl public class PyChainedComparisonsInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.chained.comparisons"); - } - - @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 { - boolean myIsLeft; - boolean myIsRight; - PyElementType myOperator; - boolean getInnerRight; - - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameChainedComparisons(); } + @Nonnull @Override - public void visitPyBinaryExpression(final PyBinaryExpression node) { - myIsLeft = false; - myIsRight = false; - myOperator = null; - getInnerRight = false; - - PyExpression leftExpression = node.getLeftExpression(); - PyExpression rightExpression = node.getRightExpression(); - - if (leftExpression instanceof PyBinaryExpression && - rightExpression instanceof PyBinaryExpression) { - if (node.getOperator() == PyTokenTypes.AND_KEYWORD) { - if (isRightSimplified((PyBinaryExpression)leftExpression, (PyBinaryExpression)rightExpression) || - isLeftSimplified((PyBinaryExpression)leftExpression, (PyBinaryExpression)rightExpression)) - registerProblem(node, "Simplify chained comparison", new ChainedComparisonsQuickFix(myIsLeft, myIsRight, - getInnerRight)); - } - } + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } - private boolean isRightSimplified(@Nonnull final PyBinaryExpression leftExpression, - @Nonnull final PyBinaryExpression rightExpression) { - final PyExpression leftRight = leftExpression.getRightExpression(); - if (leftRight instanceof PyBinaryExpression && PyTokenTypes.AND_KEYWORD == leftExpression.getOperator()) { - if (isRightSimplified((PyBinaryExpression)leftRight, rightExpression)) { - getInnerRight = true; - return true; + private static class Visitor extends PyInspectionVisitor { + boolean myIsLeft; + boolean myIsRight; + PyElementType myOperator; + boolean getInnerRight; + + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); } - } - - if (leftRight instanceof PyBinaryExpression && - PyTokenTypes.RELATIONAL_OPERATIONS.contains(((PyBinaryExpression)leftRight).getOperator())) { - if (isRightSimplified((PyBinaryExpression)leftRight, rightExpression)) - return true; - } - - myOperator = leftExpression.getOperator(); - if (PyTokenTypes.RELATIONAL_OPERATIONS.contains(myOperator)) { - if (leftRight != null) { - if (leftRight.getText().equals(getLeftExpression(rightExpression, true).getText())) { - myIsLeft = false; - myIsRight = true; - return true; - } - final PyExpression right = getSmallestRight(rightExpression, true); - if (right != null && leftRight.getText().equals(right.getText())) { + @Override + public void visitPyBinaryExpression(final PyBinaryExpression node) { myIsLeft = false; myIsRight = false; - return true; - } + myOperator = null; + getInnerRight = false; + + PyExpression leftExpression = node.getLeftExpression(); + PyExpression rightExpression = node.getRightExpression(); + + if (leftExpression instanceof PyBinaryExpression && + rightExpression instanceof PyBinaryExpression) { + if (node.getOperator() == PyTokenTypes.AND_KEYWORD) { + if (isRightSimplified((PyBinaryExpression) leftExpression, (PyBinaryExpression) rightExpression) || + isLeftSimplified((PyBinaryExpression) leftExpression, (PyBinaryExpression) rightExpression)) { + registerProblem(node, "Simplify chained comparison", new ChainedComparisonsQuickFix(myIsLeft, myIsRight, + getInnerRight + )); + } + } + } } - } - return false; - } - - private static boolean isOpposite(final PyElementType op1, final PyElementType op2) { - if ((op1 == PyTokenTypes.GT || op1 == PyTokenTypes.GE) && (op2 == PyTokenTypes.LT || op2 == PyTokenTypes.LE)) - return true; - if ((op2 == PyTokenTypes.GT || op2 == PyTokenTypes.GE) && (op1 == PyTokenTypes.LT || op1 == PyTokenTypes.LE)) - return true; - return false; - } + private boolean isRightSimplified( + @Nonnull final PyBinaryExpression leftExpression, + @Nonnull final PyBinaryExpression rightExpression + ) { + final PyExpression leftRight = leftExpression.getRightExpression(); + if (leftRight instanceof PyBinaryExpression && PyTokenTypes.AND_KEYWORD == leftExpression.getOperator()) { + if (isRightSimplified((PyBinaryExpression) leftRight, rightExpression)) { + getInnerRight = true; + return true; + } + } + + if (leftRight instanceof PyBinaryExpression && + PyTokenTypes.RELATIONAL_OPERATIONS.contains(((PyBinaryExpression) leftRight).getOperator())) { + if (isRightSimplified((PyBinaryExpression) leftRight, rightExpression)) { + return true; + } + } + + myOperator = leftExpression.getOperator(); + if (PyTokenTypes.RELATIONAL_OPERATIONS.contains(myOperator)) { + if (leftRight != null) { + if (leftRight.getText().equals(getLeftExpression(rightExpression, true).getText())) { + myIsLeft = false; + myIsRight = true; + return true; + } + + final PyExpression right = getSmallestRight(rightExpression, true); + if (right != null && leftRight.getText().equals(right.getText())) { + myIsLeft = false; + myIsRight = false; + return true; + } + } + } + return false; + } + private static boolean isOpposite(final PyElementType op1, final PyElementType op2) { + if ((op1 == PyTokenTypes.GT || op1 == PyTokenTypes.GE) && (op2 == PyTokenTypes.LT || op2 == PyTokenTypes.LE)) { + return true; + } + if ((op2 == PyTokenTypes.GT || op2 == PyTokenTypes.GE) && (op1 == PyTokenTypes.LT || op1 == PyTokenTypes.LE)) { + return true; + } - private boolean isLeftSimplified(PyBinaryExpression leftExpression, PyBinaryExpression rightExpression) { - final PyExpression leftLeft = leftExpression.getLeftExpression(); - final PyExpression leftRight = leftExpression.getRightExpression(); - if (leftRight instanceof PyBinaryExpression - && PyTokenTypes.AND_KEYWORD == leftExpression.getOperator()) { - if (isLeftSimplified((PyBinaryExpression)leftRight, rightExpression)) { - getInnerRight = true; - return true; + return false; } - } - - if (leftLeft instanceof PyBinaryExpression && - PyTokenTypes.RELATIONAL_OPERATIONS.contains(((PyBinaryExpression)leftLeft).getOperator())) { - if (isLeftSimplified((PyBinaryExpression)leftLeft, rightExpression)) - return true; - } - - myOperator = leftExpression.getOperator(); - if (PyTokenTypes.RELATIONAL_OPERATIONS.contains(myOperator)) { - if (leftLeft != null) { - if (leftLeft.getText().equals(getLeftExpression(rightExpression, false).getText())) { - myIsLeft = true; - myIsRight = true; - return true; - } - final PyExpression right = getSmallestRight(rightExpression, false); - if (right != null && leftLeft.getText().equals(right.getText())) { - myIsLeft = true; - myIsRight = false; - return true; - } + + + private boolean isLeftSimplified(PyBinaryExpression leftExpression, PyBinaryExpression rightExpression) { + final PyExpression leftLeft = leftExpression.getLeftExpression(); + final PyExpression leftRight = leftExpression.getRightExpression(); + if (leftRight instanceof PyBinaryExpression + && PyTokenTypes.AND_KEYWORD == leftExpression.getOperator()) { + if (isLeftSimplified((PyBinaryExpression) leftRight, rightExpression)) { + getInnerRight = true; + return true; + } + } + + if (leftLeft instanceof PyBinaryExpression && + PyTokenTypes.RELATIONAL_OPERATIONS.contains(((PyBinaryExpression) leftLeft).getOperator())) { + if (isLeftSimplified((PyBinaryExpression) leftLeft, rightExpression)) { + return true; + } + } + + myOperator = leftExpression.getOperator(); + if (PyTokenTypes.RELATIONAL_OPERATIONS.contains(myOperator)) { + if (leftLeft != null) { + if (leftLeft.getText().equals(getLeftExpression(rightExpression, false).getText())) { + myIsLeft = true; + myIsRight = true; + return true; + } + final PyExpression right = getSmallestRight(rightExpression, false); + if (right != null && leftLeft.getText().equals(right.getText())) { + myIsLeft = true; + myIsRight = false; + return true; + } + } + } + return false; } - } - return false; - } - private PyExpression getLeftExpression(PyBinaryExpression expression, boolean isRight) { - PyExpression result = expression; - while (result instanceof PyBinaryExpression && ( - PyTokenTypes.RELATIONAL_OPERATIONS.contains(((PyBinaryExpression)result).getOperator()) - || PyTokenTypes.EQUALITY_OPERATIONS.contains(((PyBinaryExpression)result).getOperator()))) { - - final boolean opposite = isOpposite(((PyBinaryExpression)result).getOperator(), myOperator); - if ((isRight && opposite) || (!isRight && !opposite)) - break; - result = ((PyBinaryExpression)result).getLeftExpression(); - } - return result; - } + private PyExpression getLeftExpression(PyBinaryExpression expression, boolean isRight) { + PyExpression result = expression; + while (result instanceof PyBinaryExpression && ( + PyTokenTypes.RELATIONAL_OPERATIONS.contains(((PyBinaryExpression) result).getOperator()) + || PyTokenTypes.EQUALITY_OPERATIONS.contains(((PyBinaryExpression) result).getOperator()))) { + + final boolean opposite = isOpposite(((PyBinaryExpression) result).getOperator(), myOperator); + if ((isRight && opposite) || (!isRight && !opposite)) { + break; + } + result = ((PyBinaryExpression) result).getLeftExpression(); + } + return result; + } - @Nullable - private PyExpression getSmallestRight(PyBinaryExpression expression, boolean isRight) { - PyExpression result = expression; - while (result instanceof PyBinaryExpression && ( - PyTokenTypes.RELATIONAL_OPERATIONS.contains(((PyBinaryExpression)result).getOperator()) - || PyTokenTypes.EQUALITY_OPERATIONS.contains(((PyBinaryExpression)result).getOperator()))) { - - final boolean opposite = isOpposite(((PyBinaryExpression)result).getOperator(), myOperator); - if ((isRight && !opposite) || (!isRight && opposite)) - break; - result = ((PyBinaryExpression)result).getRightExpression(); - } - return result; + @Nullable + private PyExpression getSmallestRight(PyBinaryExpression expression, boolean isRight) { + PyExpression result = expression; + while (result instanceof PyBinaryExpression && ( + PyTokenTypes.RELATIONAL_OPERATIONS.contains(((PyBinaryExpression) result).getOperator()) + || PyTokenTypes.EQUALITY_OPERATIONS.contains(((PyBinaryExpression) result).getOperator()))) { + + final boolean opposite = isOpposite(((PyBinaryExpression) result).getOperator(), myOperator); + if ((isRight && !opposite) || (!isRight && opposite)) { + break; + } + result = ((PyBinaryExpression) result).getRightExpression(); + } + return result; + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyClassHasNoInitInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyClassHasNoInitInspection.java index 3894495a..372f41f7 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyClassHasNoInitInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyClassHasNoInitInspection.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.impl.inspections.quickfix.AddMethodQuickFix; import com.jetbrains.python.psi.PyClass; @@ -27,67 +26,72 @@ import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.util.PsiTreeUtil; +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import consulo.util.lang.StringUtil; -import org.jetbrains.annotations.Nls; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; + import java.util.List; /** - * User: ktisha * See pylint W0232 + * + * @author ktisha */ @ExtensionImpl public class PyClassHasNoInitInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.class.has.no.init"); - } - - @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.inspNameClassHasNoInit(); } + @Nonnull @Override - public void visitPyClass(PyClass node) { - final PyClass outerClass = PsiTreeUtil.getParentOfType(node, PyClass.class); - assert node != null; - if (outerClass != null && StringUtil.equalsIgnoreCase("meta", node.getName())) { - return; - } - final List types = node.getAncestorTypes(myTypeEvalContext); - for (PyClassLikeType type : types) { - if (type == null) { - return; - } - final String qName = type.getClassQName(); - if (qName != null && qName.contains(PyNames.TEST_CASE)) { - return; - } - if (!(type instanceof PyClassType)) { - return; + 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); } - } - final PyFunction init = node.findInitOrNew(true, null); - if (init == null) { - registerProblem(node.getNameIdentifier(), - PyBundle.message("INSP.class.has.no.init"), - new AddMethodQuickFix("__init__", node.getName(), false)); - } + @Override + public void visitPyClass(PyClass node) { + final PyClass outerClass = PsiTreeUtil.getParentOfType(node, PyClass.class); + assert node != null; + if (outerClass != null && StringUtil.equalsIgnoreCase("meta", node.getName())) { + return; + } + final List types = node.getAncestorTypes(myTypeEvalContext); + for (PyClassLikeType type : types) { + if (type == null) { + return; + } + final String qName = type.getClassQName(); + if (qName != null && qName.contains(PyNames.TEST_CASE)) { + return; + } + if (!(type instanceof PyClassType)) { + return; + } + } + + final PyFunction init = node.findInitOrNew(true, null); + if (init == null) { + registerProblem( + node.getNameIdentifier(), + PyLocalize.inspClassHasNoInit().get(), + new AddMethodQuickFix("__init__", node.getName(), false) + ); + } + } } - } } \ No newline at end of file diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyClassicStyleClassInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyClassicStyleClassInspection.java index 57d65184..d967ffa7 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyClassicStyleClassInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyClassicStyleClassInspection.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.inspections.quickfix.TransformClassicClassQuickFix; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyExpression; @@ -24,8 +23,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,46 +33,49 @@ */ @ExtensionImpl public class PyClassicStyleClassInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.classic.class.usage"); - } - - @Override - public boolean isEnabledByDefault() { - return false; - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameClassicClassUsage(); + } - private static class Visitor extends PyInspectionVisitor { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); + @Override + public boolean isEnabledByDefault() { + return false; } + @Nonnull @Override - public void visitPyClass(PyClass node) { - final ASTNode nameNode = node.getNameNode(); - if (!node.isNewStyleClass(myTypeEvalContext) && nameNode != null) { - PyExpression[] superClassExpressions = node.getSuperClassExpressions(); - if (superClassExpressions.length == 0) { - registerProblem(nameNode.getPsi(), - PyBundle.message("INSP.classic.class.usage.old.style.class"), - new TransformClassicClassQuickFix()); + 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); } - else { - registerProblem(nameNode.getPsi(), PyBundle.message("INSP.classic.class.usage.old.style.class.ancestors")); + + @Override + public void visitPyClass(PyClass node) { + final ASTNode nameNode = node.getNameNode(); + if (!node.isNewStyleClass(myTypeEvalContext) && nameNode != null) { + PyExpression[] superClassExpressions = node.getSuperClassExpressions(); + if (superClassExpressions.length == 0) { + registerProblem( + nameNode.getPsi(), + PyLocalize.inspClassicClassUsageOldStyleClass().get(), + new TransformClassicClassQuickFix() + ); + } + else { + registerProblem(nameNode.getPsi(), PyLocalize.inspClassicClassUsageOldStyleClassAncestors().get()); + } + } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyComparisonWithNoneInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyComparisonWithNoneInspection.java index 8cf7570a..9ebb2628 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyComparisonWithNoneInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyComparisonWithNoneInspection.java @@ -15,20 +15,19 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.impl.inspections.quickfix.ComparisonWithNoneQuickFix; -import com.jetbrains.python.psi.*; import com.jetbrains.python.impl.psi.impl.PyBuiltinCache; +import com.jetbrains.python.psi.*; 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.PsiReference; -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 +36,42 @@ */ @ExtensionImpl public class PyComparisonWithNoneInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.comparison.with.none"); - } - - @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.inspNameComparisonWithNone(); } + @Nonnull @Override - public void visitPyBinaryExpression(PyBinaryExpression node) { - final PyExpression rightExpression = node.getRightExpression(); - if ((rightExpression instanceof PyReferenceExpression && PyNames.NONE.equals(rightExpression.getText())) || rightExpression instanceof PyNoneLiteralExpression) { - final PyElementType operator = node.getOperator(); - if (operator == PyTokenTypes.EQEQ || operator == PyTokenTypes.NE || operator == PyTokenTypes.NE_OLD) { - PsiReference reference = node.getReference(); - assert reference != null; - PsiElement result = reference.resolve(); - if (result == null || PyBuiltinCache.getInstance(node).isBuiltin(result)) { - registerProblem(node, "Comparison with None performed with equality operators", new ComparisonWithNoneQuickFix()); - } + 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 visitPyBinaryExpression(PyBinaryExpression node) { + final PyExpression rightExpression = node.getRightExpression(); + if ((rightExpression instanceof PyReferenceExpression && PyNames.NONE.equals(rightExpression.getText())) || rightExpression instanceof PyNoneLiteralExpression) { + final PyElementType operator = node.getOperator(); + if (operator == PyTokenTypes.EQEQ || operator == PyTokenTypes.NE || operator == PyTokenTypes.NE_OLD) { + PsiReference reference = node.getReference(); + assert reference != null; + PsiElement result = reference.resolve(); + if (result == null || PyBuiltinCache.getInstance(node).isBuiltin(result)) { + registerProblem(node, "Comparison with None performed with equality operators", new ComparisonWithNoneQuickFix()); + } + } + } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyCompatibilityInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyCompatibilityInspection.java index 1384c3b3..a9f48e81 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyCompatibilityInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyCompatibilityInspection.java @@ -16,16 +16,15 @@ package com.jetbrains.python.impl.inspections; import com.google.common.collect.ImmutableList; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; -import com.jetbrains.python.psi.*; import com.jetbrains.python.impl.psi.impl.PyBuiltinCache; -import com.jetbrains.python.psi.types.PyClassType; -import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.impl.psi.types.PyTypeChecker; -import com.jetbrains.python.psi.types.TypeEvalContext; import com.jetbrains.python.impl.validation.CompatibilityVisitor; import com.jetbrains.python.impl.validation.UnsupportedFeaturesUtil; +import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.types.PyClassType; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.TypeEvalContext; import consulo.annotation.component.ExtensionImpl; import consulo.document.util.TextRange; import consulo.language.editor.inspection.*; @@ -34,334 +33,310 @@ import consulo.language.psi.*; import consulo.language.psi.util.PsiTreeUtil; import consulo.language.psi.util.QualifiedName; +import consulo.localize.LocalizeValue; import consulo.module.content.ProjectRootManager; +import consulo.python.impl.localize.PyLocalize; import consulo.util.collection.ArrayUtil; import consulo.virtualFileSystem.VirtualFile; -import org.jetbrains.annotations.Nls; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; + import java.util.*; /** - * User: catherine - *

* Inspection to detect code incompatibility with python versions + * + * @author catherine */ @ExtensionImpl -public class PyCompatibilityInspection extends PyInspection -{ - @Nonnull - public static final List BACKPORTED_PACKAGES = List.of("enum", "typing"); - - public static final int LATEST_INSPECTION_VERSION = 2; - - @Nonnull - public static final List DEFAULT_PYTHON_VERSIONS = ImmutableList.of(LanguageLevel.PYTHON27, LanguageLevel.getLatest()); - - @Nullable - public static PyCompatibilityInspection getInstance(@Nonnull PsiElement element) - { - final InspectionProfile inspectionProfile = InspectionProjectProfileManager.getInstance(element.getProject()).getInspectionProfile(); - final String toolName = PyCompatibilityInspection.class.getSimpleName(); - return (PyCompatibilityInspection) inspectionProfile.getUnwrappedTool(toolName, element); - } - - @Nonnull - @Override - public InspectionToolState createStateProvider() - { - return new PyCompatibilityInspectionState(); - } - - @Override - public boolean isEnabledByDefault() - { - return false; - } - - private List updateVersionsToProcess(PyCompatibilityInspectionState state) - { - List result = new ArrayList<>(); - - for(String version : state.versions) - { - LanguageLevel level = LanguageLevel.fromPythonVersion(version); - result.add(level); - } - return result; - } - - @Nls - @Nonnull - @Override - public String getDisplayName() - { - return PyBundle.message("INSP.NAME.compatibility"); - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, boolean isOnTheFly, LocalInspectionToolSession session, Object state) - { - return new Visitor(holder, updateVersionsToProcess((PyCompatibilityInspectionState) state)); - } - - private static class Visitor extends CompatibilityVisitor - { - private final ProblemsHolder myHolder; - private Set myUsedImports = Collections.synchronizedSet(new HashSet()); - - public Visitor(ProblemsHolder holder, List versionsToProcess) - { - super(versionsToProcess); - myHolder = holder; - } - - @Override - protected void registerProblem(@Nonnull PsiElement element, - @Nonnull TextRange range, - @Nonnull String message, - @Nullable LocalQuickFix quickFix, - boolean asError) - { - if(element.getTextLength() == 0) - { - return; - } - - range = range.shiftRight(-element.getTextRange().getStartOffset()); - if(quickFix != null) - { - myHolder.registerProblem(element, range, message, quickFix); - } - else - { - myHolder.registerProblem(element, range, message); - } - } - - @Override - public void visitPyCallExpression(PyCallExpression node) - { - super.visitPyCallExpression(node); - - final Optional optionalFunction = Optional.ofNullable(node.getCallee()) - .map(PyExpression::getReference) - .map(PsiReference::resolve) - .filter(PyFunction.class::isInstance) - .map - (PyFunction.class::cast); - - if(optionalFunction.isPresent()) - { - final PyFunction function = optionalFunction.get(); - final PyClass containingClass = function.getContainingClass(); - final String originalFunctionName = function.getName(); - - final String functionName = - containingClass != null && PyNames.INIT.equals(originalFunctionName) ? node.getCallee().getText() : originalFunctionName; - - if(containingClass != null) - { - final String className = containingClass.getName(); - - if(UnsupportedFeaturesUtil.CLASS_METHODS.containsKey(className)) - { - final Map> unsupportedMethods = UnsupportedFeaturesUtil.CLASS_METHODS.get(className); - - registerForAllMatchingVersions(level -> unsupportedMethods.getOrDefault(level, Collections.emptySet()).contains(functionName), - " not have method " + functionName, - node, - null); - } - } - - if(PyBuiltinCache.getInstance(function).isBuiltin(function) && - !"print".equals(functionName) && - !"exec".equals(functionName) && - !myUsedImports.contains(functionName)) - { - registerForAllMatchingVersions(level -> UnsupportedFeaturesUtil.BUILTINS.get(level).contains(functionName), - " not have method " + functionName, - node, - null); - } - } - } - - @Override - public void visitPyImportElement(PyImportElement importElement) - { - myUsedImports.add(importElement.getVisibleName()); - - final PyIfStatement ifParent = PsiTreeUtil.getParentOfType(importElement, PyIfStatement.class); - if(ifParent != null) - { - return; - } - - final PyTryExceptStatement tryExceptStatement = PsiTreeUtil.getParentOfType(importElement, PyTryExceptStatement.class); - if(tryExceptStatement != null) - { - for(PyExceptPart part : tryExceptStatement.getExceptParts()) - { - final PyExpression exceptClass = part.getExceptClass(); - if(exceptClass != null && exceptClass.getText().equals("ImportError")) - { - return; - } - } - } - - final PyFromImportStatement fromImportStatement = PsiTreeUtil.getParentOfType(importElement, PyFromImportStatement.class); - if(fromImportStatement != null) - { - final QualifiedName qName = importElement.getImportedQName(); - final QualifiedName sourceQName = fromImportStatement.getImportSourceQName(); - - if(qName != null && sourceQName != null && qName.matches("unicode_literals") && sourceQName.matches("__future__")) - { - registerForAllMatchingVersions(level -> level.isOlderThan(LanguageLevel.PYTHON26), - " not have unicode_literals in __future__ module", - importElement, - null); - } - - return; - } - - final QualifiedName qName = importElement.getImportedQName(); - if(qName != null && !qName.matches("builtins") && !qName.matches("__builtin__")) - { - final String moduleName = qName.toString(); - - registerForAllMatchingVersions(level -> UnsupportedFeaturesUtil.MODULES.get(level) - .contains(moduleName) && !BACKPORTED_PACKAGES.contains( - moduleName), " not have module " + - moduleName, importElement, null); - } - } - - @Override - public void visitPyFromImportStatement(PyFromImportStatement node) - { - super.visitPyFromImportStatement(node); - - if(node.getRelativeLevel() > 0) - { - return; - } - - final QualifiedName name = node.getImportSourceQName(); - final PyReferenceExpression source = node.getImportSource(); - if(name != null && source != null) - { - final String moduleName = name.toString(); - - registerForAllMatchingVersions(level -> UnsupportedFeaturesUtil.MODULES.get(level) - .contains(moduleName) && !BACKPORTED_PACKAGES.contains( - moduleName), " not have module " + name, - source, null); - } - } - - @Override - public void visitPyArgumentList(final PyArgumentList node) - { //PY-5588 - if(node.getParent() instanceof PyClass) - { - final boolean isPy3 = LanguageLevel.forElement(node).isPy3K(); - if(myVersionsToProcess.stream().anyMatch(level -> level.isOlderThan(LanguageLevel.PYTHON30)) || !isPy3) - { - Arrays.stream(node.getArguments()) - .filter(PyKeywordArgument.class::isInstance) - .forEach(expression -> myHolder.registerProblem(expression, "This syntax available only since py3", - isPy3 ? ProblemHighlightType.GENERIC_ERROR_OR_WARNING : ProblemHighlightType.GENERIC_ERROR)); - } - } - } - - @Override - public void visitPyReferenceExpression(PyReferenceExpression node) - { - super.visitPyElement(node); - - if(myVersionsToProcess.stream().anyMatch(LanguageLevel::isPy3K)) - { - final String nodeText = node.getText(); - - if(nodeText.endsWith("iteritems") || nodeText.endsWith("iterkeys") || nodeText.endsWith("itervalues")) - { - final PyExpression qualifier = node.getQualifier(); - if(qualifier != null) - { - final TypeEvalContext context = TypeEvalContext.codeAnalysis(node.getProject(), node.getContainingFile()); - final PyType type = context.getType(qualifier); - final PyClassType dictType = PyBuiltinCache.getInstance(node).getDictType(); - if(PyTypeChecker.match(dictType, type, context)) - { - registerProblem(node, "dict.iterkeys(), dict.iteritems() and dict.itervalues() methods are not available in py3"); - } - } - } - - if(PyNames.BASESTRING.equals(nodeText)) - { - final PsiElement res = node.getReference().resolve(); - if(res != null) - { - final PsiFile file = res.getContainingFile(); - if(file != null) - { - final VirtualFile virtualFile = file.getVirtualFile(); - if(virtualFile != null && ProjectRootManager.getInstance(node.getProject()).getFileIndex().isInLibraryClasses(virtualFile)) - { - registerProblem(node, "basestring type is not available in py3"); - } - } - else - { - registerProblem(node, "basestring type is not available in py3"); - } - } - } - } - } - - @Override - public void visitPyTargetExpression(PyTargetExpression node) - { - super.visitPyTargetExpression(node); - warnAboutAsyncAndAwaitInPy35AndPy36(node); - } - - @Override - public void visitPyClass(PyClass node) - { - super.visitPyClass(node); - warnAboutAsyncAndAwaitInPy35AndPy36(node); - } - - @Override - public void visitPyFunction(PyFunction node) - { - super.visitPyFunction(node); - warnAboutAsyncAndAwaitInPy35AndPy36(node); - } - - private void warnAboutAsyncAndAwaitInPy35AndPy36(@Nonnull PsiNameIdentifierOwner nameIdentifierOwner) - { - final PsiElement nameIdentifier = nameIdentifierOwner.getNameIdentifier(); - - if(nameIdentifier != null && ArrayUtil.contains(nameIdentifierOwner.getName(), PyNames.AWAIT, PyNames.ASYNC)) - { - registerOnFirstMatchingVersion(level -> LanguageLevel.PYTHON35.equals(level) || LanguageLevel.PYTHON36.equals(level), - "'async' and 'await' are not recommended to be used as variable," + - " class, function or module names. " + "They will become proper keywords in Python 3.7.", - nameIdentifier, - new PyRenameElementQuickFix()); - } - } - } +public class PyCompatibilityInspection extends PyInspection { + @Nonnull + public static final List BACKPORTED_PACKAGES = List.of("enum", "typing"); + + public static final int LATEST_INSPECTION_VERSION = 2; + + @Nonnull + public static final List DEFAULT_PYTHON_VERSIONS = ImmutableList.of(LanguageLevel.PYTHON27, LanguageLevel.getLatest()); + + @Nullable + public static PyCompatibilityInspection getInstance(@Nonnull PsiElement element) { + final InspectionProfile inspectionProfile = + InspectionProjectProfileManager.getInstance(element.getProject()).getInspectionProfile(); + final String toolName = PyCompatibilityInspection.class.getSimpleName(); + return (PyCompatibilityInspection) inspectionProfile.getUnwrappedTool(toolName, element); + } + + @Nonnull + @Override + public InspectionToolState createStateProvider() { + return new PyCompatibilityInspectionState(); + } + + @Override + public boolean isEnabledByDefault() { + return false; + } + + private List updateVersionsToProcess(PyCompatibilityInspectionState state) { + List result = new ArrayList<>(); + + for (String version : state.versions) { + LanguageLevel level = LanguageLevel.fromPythonVersion(version); + result.add(level); + } + return result; + } + + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameCompatibility(); + } + + @Nonnull + @Override + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, updateVersionsToProcess((PyCompatibilityInspectionState) state)); + } + + private static class Visitor extends CompatibilityVisitor { + private final ProblemsHolder myHolder; + private Set myUsedImports = Collections.synchronizedSet(new HashSet()); + + public Visitor(ProblemsHolder holder, List versionsToProcess) { + super(versionsToProcess); + myHolder = holder; + } + + @Override + protected void registerProblem( + @Nonnull PsiElement element, + @Nonnull TextRange range, + @Nonnull String message, + @Nullable LocalQuickFix quickFix, + boolean asError + ) { + if (element.getTextLength() == 0) { + return; + } + + range = range.shiftRight(-element.getTextRange().getStartOffset()); + if (quickFix != null) { + myHolder.registerProblem(element, range, message, quickFix); + } + else { + myHolder.registerProblem(element, range, message); + } + } + + @Override + public void visitPyCallExpression(PyCallExpression node) { + super.visitPyCallExpression(node); + + final Optional optionalFunction = Optional.ofNullable(node.getCallee()) + .map(PyExpression::getReference) + .map(PsiReference::resolve) + .filter(PyFunction.class::isInstance) + .map + (PyFunction.class::cast); + + if (optionalFunction.isPresent()) { + final PyFunction function = optionalFunction.get(); + final PyClass containingClass = function.getContainingClass(); + final String originalFunctionName = function.getName(); + + final String functionName = + containingClass != null && PyNames.INIT.equals(originalFunctionName) ? node.getCallee() + .getText() : originalFunctionName; + + if (containingClass != null) { + final String className = containingClass.getName(); + + if (UnsupportedFeaturesUtil.CLASS_METHODS.containsKey(className)) { + final Map> unsupportedMethods = UnsupportedFeaturesUtil.CLASS_METHODS.get(className); + + registerForAllMatchingVersions( + level -> unsupportedMethods.getOrDefault(level, Collections.emptySet()).contains(functionName), + " not have method " + functionName, + node, + null + ); + } + } + + if (PyBuiltinCache.getInstance(function).isBuiltin(function) && + !"print".equals(functionName) && + !"exec".equals(functionName) && + !myUsedImports.contains(functionName)) { + registerForAllMatchingVersions( + level -> UnsupportedFeaturesUtil.BUILTINS.get(level).contains(functionName), + " not have method " + functionName, + node, + null + ); + } + } + } + + @Override + public void visitPyImportElement(PyImportElement importElement) { + myUsedImports.add(importElement.getVisibleName()); + + final PyIfStatement ifParent = PsiTreeUtil.getParentOfType(importElement, PyIfStatement.class); + if (ifParent != null) { + return; + } + + final PyTryExceptStatement tryExceptStatement = PsiTreeUtil.getParentOfType(importElement, PyTryExceptStatement.class); + if (tryExceptStatement != null) { + for (PyExceptPart part : tryExceptStatement.getExceptParts()) { + final PyExpression exceptClass = part.getExceptClass(); + if (exceptClass != null && exceptClass.getText().equals("ImportError")) { + return; + } + } + } + + final PyFromImportStatement fromImportStatement = PsiTreeUtil.getParentOfType(importElement, PyFromImportStatement.class); + if (fromImportStatement != null) { + final QualifiedName qName = importElement.getImportedQName(); + final QualifiedName sourceQName = fromImportStatement.getImportSourceQName(); + + if (qName != null && sourceQName != null && qName.matches("unicode_literals") && sourceQName.matches("__future__")) { + registerForAllMatchingVersions( + level -> level.isOlderThan(LanguageLevel.PYTHON26), + " not have unicode_literals in __future__ module", + importElement, + null + ); + } + + return; + } + + final QualifiedName qName = importElement.getImportedQName(); + if (qName != null && !qName.matches("builtins") && !qName.matches("__builtin__")) { + final String moduleName = qName.toString(); + + registerForAllMatchingVersions(level -> UnsupportedFeaturesUtil.MODULES.get(level) + .contains(moduleName) && !BACKPORTED_PACKAGES.contains( + moduleName), " not have module " + + moduleName, importElement, null); + } + } + + @Override + public void visitPyFromImportStatement(PyFromImportStatement node) { + super.visitPyFromImportStatement(node); + + if (node.getRelativeLevel() > 0) { + return; + } + + final QualifiedName name = node.getImportSourceQName(); + final PyReferenceExpression source = node.getImportSource(); + if (name != null && source != null) { + final String moduleName = name.toString(); + + registerForAllMatchingVersions(level -> UnsupportedFeaturesUtil.MODULES.get(level) + .contains(moduleName) && !BACKPORTED_PACKAGES.contains( + moduleName), " not have module " + name, + source, null + ); + } + } + + @Override + public void visitPyArgumentList(final PyArgumentList node) { //PY-5588 + if (node.getParent() instanceof PyClass) { + final boolean isPy3 = LanguageLevel.forElement(node).isPy3K(); + if (myVersionsToProcess.stream().anyMatch(level -> level.isOlderThan(LanguageLevel.PYTHON30)) || !isPy3) { + Arrays.stream(node.getArguments()) + .filter(PyKeywordArgument.class::isInstance) + .forEach(expression -> myHolder.registerProblem(expression, "This syntax available only since py3", + isPy3 ? ProblemHighlightType.GENERIC_ERROR_OR_WARNING : ProblemHighlightType.GENERIC_ERROR + )); + } + } + } + + @Override + public void visitPyReferenceExpression(PyReferenceExpression node) { + super.visitPyElement(node); + + if (myVersionsToProcess.stream().anyMatch(LanguageLevel::isPy3K)) { + final String nodeText = node.getText(); + + if (nodeText.endsWith("iteritems") || nodeText.endsWith("iterkeys") || nodeText.endsWith("itervalues")) { + final PyExpression qualifier = node.getQualifier(); + if (qualifier != null) { + final TypeEvalContext context = TypeEvalContext.codeAnalysis(node.getProject(), node.getContainingFile()); + final PyType type = context.getType(qualifier); + final PyClassType dictType = PyBuiltinCache.getInstance(node).getDictType(); + if (PyTypeChecker.match(dictType, type, context)) { + registerProblem( + node, + "dict.iterkeys(), dict.iteritems() and dict.itervalues() methods are not available in py3" + ); + } + } + } + + if (PyNames.BASESTRING.equals(nodeText)) { + final PsiElement res = node.getReference().resolve(); + if (res != null) { + final PsiFile file = res.getContainingFile(); + if (file != null) { + final VirtualFile virtualFile = file.getVirtualFile(); + if (virtualFile != null && ProjectRootManager.getInstance(node.getProject()) + .getFileIndex() + .isInLibraryClasses(virtualFile)) { + registerProblem(node, "basestring type is not available in py3"); + } + } + else { + registerProblem(node, "basestring type is not available in py3"); + } + } + } + } + } + + @Override + public void visitPyTargetExpression(PyTargetExpression node) { + super.visitPyTargetExpression(node); + warnAboutAsyncAndAwaitInPy35AndPy36(node); + } + + @Override + public void visitPyClass(PyClass node) { + super.visitPyClass(node); + warnAboutAsyncAndAwaitInPy35AndPy36(node); + } + + @Override + public void visitPyFunction(PyFunction node) { + super.visitPyFunction(node); + warnAboutAsyncAndAwaitInPy35AndPy36(node); + } + + private void warnAboutAsyncAndAwaitInPy35AndPy36(@Nonnull PsiNameIdentifierOwner nameIdentifierOwner) { + final PsiElement nameIdentifier = nameIdentifierOwner.getNameIdentifier(); + + if (nameIdentifier != null && ArrayUtil.contains(nameIdentifierOwner.getName(), PyNames.AWAIT, PyNames.ASYNC)) { + registerOnFirstMatchingVersion( + level -> LanguageLevel.PYTHON35.equals(level) || LanguageLevel.PYTHON36.equals(level), + "'async' and 'await' are not recommended to be used as variable," + + " class, function or module names. " + "They will become proper keywords in Python 3.7.", + nameIdentifier, + new PyRenameElementQuickFix() + ); + } + } + } } \ No newline at end of file diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDecoratorInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDecoratorInspection.java index f09b34f6..5c8461b6 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDecoratorInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDecoratorInspection.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.RemoveDecoratorQuickFix; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyDecorator; @@ -26,54 +24,57 @@ 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; /** - * User: catherine - *

- * Inspection to detect occurrences of @classmethod and @staticmethod - * on methods outside of a class + * Inspection to detect occurrences of @classmethod and @staticmethod on methods outside of a class. + * + * @author catherine */ @ExtensionImpl public class PyDecoratorInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.decorator.outside.class"); - } - - @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.inspNameDecoratorOutsideClass(); } + @Nonnull @Override - public void visitPyFunction(final PyFunction node) { - PyClass containingClass = node.getContainingClass(); - if (containingClass != null) - return; + 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 visitPyFunction(final PyFunction node) { + PyClass containingClass = node.getContainingClass(); + if (containingClass != null) { + return; + } - PyDecoratorList decorators = node.getDecoratorList(); - if (decorators == null) - return; - for (PyDecorator decorator : decorators.getDecorators()) { - String name = decorator.getText(); - if (name.equals("@classmethod") || name.equals("@staticmethod")) - registerProblem(decorator, "Decorator " + name + " on method outside class", new RemoveDecoratorQuickFix()); - } + PyDecoratorList decorators = node.getDecoratorList(); + if (decorators == null) { + return; + } + for (PyDecorator decorator : decorators.getDecorators()) { + String name = decorator.getText(); + if (name.equals("@classmethod") || name.equals("@staticmethod")) { + registerProblem(decorator, "Decorator " + name + " on method outside class", new RemoveDecoratorQuickFix()); + } + } + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDefaultArgumentInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDefaultArgumentInspection.java index 08668729..068dd9ae 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDefaultArgumentInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDefaultArgumentInspection.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 com.jetbrains.python.impl.inspections.quickfix.PyDefaultArgumentQuickFix; import com.jetbrains.python.psi.*; 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; @@ -33,40 +31,42 @@ */ @ExtensionImpl public class PyDefaultArgumentInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.default.argument"); - } - - @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.inspNameDefaultArgument(); } + @Nonnull @Override - public void visitPyNamedParameter(PyNamedParameter node) { - PyExpression defaultValue = node.getDefaultValue(); - if (defaultValue != null) { - if (defaultValue instanceof PyListLiteralExpression || defaultValue instanceof PyDictLiteralExpression) { - registerProblem(defaultValue, "Default argument value is mutable", new PyDefaultArgumentQuickFix()); + 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); } - if (defaultValue instanceof PyCallExpression) { - PyExpression callee = ((PyCallExpression)defaultValue).getCallee(); - if (callee != null && "dict".equals(callee.getText())) - registerProblem(defaultValue, "Default argument value is mutable", new PyDefaultArgumentQuickFix()); + + @Override + public void visitPyNamedParameter(PyNamedParameter node) { + PyExpression defaultValue = node.getDefaultValue(); + if (defaultValue != null) { + if (defaultValue instanceof PyListLiteralExpression || defaultValue instanceof PyDictLiteralExpression) { + registerProblem(defaultValue, "Default argument value is mutable", new PyDefaultArgumentQuickFix()); + } + if (defaultValue instanceof PyCallExpression) { + PyExpression callee = ((PyCallExpression) defaultValue).getCallee(); + if (callee != null && "dict".equals(callee.getText())) { + registerProblem(defaultValue, "Default argument value is mutable", new PyDefaultArgumentQuickFix()); + } + } + } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDeprecationInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDeprecationInspection.java index 8858ba6e..677a7a0c 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDeprecationInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDeprecationInspection.java @@ -25,8 +25,7 @@ import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiPolyVariantReference; import consulo.language.psi.util.PsiTreeUtil; -import org.jetbrains.annotations.Nls; - +import consulo.localize.LocalizeValue; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -35,59 +34,64 @@ */ @ExtensionImpl public class PyDeprecationInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return "Deprecated function, class or module"; - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - final 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 LocalizeValue.localizeTODO("Deprecated function, class or module"); } + @Nonnull @Override - public void visitPyReferenceExpression(PyReferenceExpression node) { - final PyExceptPart exceptPart = PsiTreeUtil.getParentOfType(node, PyExceptPart.class); - if (exceptPart != null) { - final PyExpression exceptClass = exceptPart.getExceptClass(); - if (exceptClass != null && "ImportError".equals(exceptClass.getText())) { - return; + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + final 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); } - } - final PsiPolyVariantReference reference = node.getReference(getResolveContext()); - if (reference == null) { - return; - } - final PsiElement resolveResult = reference.resolve(); - final PyFromImportStatement importStatement = PsiTreeUtil.getParentOfType(node, PyFromImportStatement.class); - if (importStatement != null) { - final PsiElement element = importStatement.resolveImportSource(); - if (resolveResult != null && element != resolveResult.getContainingFile()) { - return; + + @Override + public void visitPyReferenceExpression(PyReferenceExpression node) { + final PyExceptPart exceptPart = PsiTreeUtil.getParentOfType(node, PyExceptPart.class); + if (exceptPart != null) { + final PyExpression exceptClass = exceptPart.getExceptClass(); + if (exceptClass != null && "ImportError".equals(exceptClass.getText())) { + return; + } + } + final PsiPolyVariantReference reference = node.getReference(getResolveContext()); + if (reference == null) { + return; + } + final PsiElement resolveResult = reference.resolve(); + final PyFromImportStatement importStatement = PsiTreeUtil.getParentOfType(node, PyFromImportStatement.class); + if (importStatement != null) { + final PsiElement element = importStatement.resolveImportSource(); + if (resolveResult != null && element != resolveResult.getContainingFile()) { + return; + } + } + String deprecationMessage = null; + if (resolveResult instanceof PyFunction) { + deprecationMessage = ((PyFunction) resolveResult).getDeprecationMessage(); + } + else if (resolveResult instanceof PyFile) { + deprecationMessage = ((PyFile) resolveResult).getDeprecationMessage(); + } + if (deprecationMessage != null) { + ASTNode nameElement = node.getNameElement(); + registerProblem( + nameElement == null ? node : nameElement.getPsi(), + deprecationMessage, + ProblemHighlightType.LIKE_DEPRECATED + ); + } } - } - String deprecationMessage = null; - if (resolveResult instanceof PyFunction) { - deprecationMessage = ((PyFunction)resolveResult).getDeprecationMessage(); - } - else if (resolveResult instanceof PyFile) { - deprecationMessage = ((PyFile)resolveResult).getDeprecationMessage(); - } - if (deprecationMessage != null) { - ASTNode nameElement = node.getNameElement(); - registerProblem(nameElement == null ? node : nameElement.getPsi(), deprecationMessage, ProblemHighlightType.LIKE_DEPRECATED); - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDictCreationInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDictCreationInspection.java index 0dbc4d15..01aade7b 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDictCreationInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDictCreationInspection.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.DictCreationQuickFix; import com.jetbrains.python.psi.*; import consulo.annotation.component.ExtensionImpl; @@ -26,11 +24,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.lang.Pair; -import org.jetbrains.annotations.Nls; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; + import java.util.ArrayList; import java.util.List; @@ -39,88 +38,97 @@ */ @ExtensionImpl public class PyDictCreationInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.dict.creation"); - } - - @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.inspNameDictCreation(); } + @Nonnull @Override - public void visitPyAssignmentStatement(PyAssignmentStatement node) { - if (node.getAssignedValue() instanceof PyDictLiteralExpression) { - if (node.getTargets().length != 1) { - return; - } - final PyExpression target = node.getTargets()[0]; - final String name = target.getName(); - if (name == null) { - return; + 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); } - PyStatement statement = PsiTreeUtil.getNextSiblingOfType(node, PyStatement.class); + @Override + public void visitPyAssignmentStatement(PyAssignmentStatement node) { + if (node.getAssignedValue() instanceof PyDictLiteralExpression) { + if (node.getTargets().length != 1) { + return; + } + final PyExpression target = node.getTargets()[0]; + final String name = target.getName(); + if (name == null) { + return; + } - while (statement instanceof PyAssignmentStatement) { - final PyAssignmentStatement assignmentStatement = (PyAssignmentStatement)statement; - final List> targets = getDictTargets(target, name, assignmentStatement); - if (targets == null) - return; - if (!targets.isEmpty()) { - registerProblem(node, "This dictionary creation could be rewritten as a dictionary literal", new DictCreationQuickFix(node)); - break; - } - statement = PsiTreeUtil.getNextSiblingOfType(assignmentStatement, PyStatement.class); + PyStatement statement = PsiTreeUtil.getNextSiblingOfType(node, PyStatement.class); + + while (statement instanceof PyAssignmentStatement) { + final PyAssignmentStatement assignmentStatement = (PyAssignmentStatement) statement; + final List> targets = getDictTargets(target, name, assignmentStatement); + if (targets == null) { + return; + } + if (!targets.isEmpty()) { + registerProblem( + node, + "This dictionary creation could be rewritten as a dictionary literal", + new DictCreationQuickFix(node) + ); + break; + } + statement = PsiTreeUtil.getNextSiblingOfType(assignmentStatement, PyStatement.class); + } + } } - } } - } - @Nullable - public static List> getDictTargets(@Nonnull final PyExpression target, - @Nonnull final String name, - @Nonnull final PyAssignmentStatement assignmentStatement) { - final List> targets = new ArrayList>(); - for (Pair targetToValue : assignmentStatement.getTargetsToValuesMapping()) { - if (targetToValue.first instanceof PySubscriptionExpression) { - final PySubscriptionExpression subscriptionExpression = (PySubscriptionExpression)targetToValue.first; - if (name.equals(subscriptionExpression.getOperand().getName()) && - subscriptionExpression.getIndexExpression() != null && - !referencesTarget(targetToValue.second, target)) { - targets.add(targetToValue); + @Nullable + public static List> getDictTargets( + @Nonnull final PyExpression target, + @Nonnull final String name, + @Nonnull final PyAssignmentStatement assignmentStatement + ) { + final List> targets = new ArrayList>(); + for (Pair targetToValue : assignmentStatement.getTargetsToValuesMapping()) { + if (targetToValue.first instanceof PySubscriptionExpression) { + final PySubscriptionExpression subscriptionExpression = (PySubscriptionExpression) targetToValue.first; + if (name.equals(subscriptionExpression.getOperand().getName()) && + subscriptionExpression.getIndexExpression() != null && + !referencesTarget(targetToValue.second, target)) { + targets.add(targetToValue); + } + } + else { + return null; + } } - } - else - return null; + return targets; } - return targets; - } - private static boolean referencesTarget(@Nonnull final PyExpression expression, @Nonnull final PsiElement target) { - final List refs = new ArrayList(); - expression.accept(new PyRecursiveElementVisitor() { - @Override - public void visitPyReferenceExpression(PyReferenceExpression node) { - super.visitPyReferenceExpression(node); - final PsiPolyVariantReference ref = node.getReference(); - if (ref.isReferenceTo(target)) { - refs.add(node); - } - } - }); - return !refs.isEmpty(); - } + private static boolean referencesTarget(@Nonnull final PyExpression expression, @Nonnull final PsiElement target) { + final List refs = new ArrayList(); + expression.accept(new PyRecursiveElementVisitor() { + @Override + public void visitPyReferenceExpression(PyReferenceExpression node) { + super.visitPyReferenceExpression(node); + final PsiPolyVariantReference ref = node.getReference(); + if (ref.isReferenceTo(target)) { + refs.add(node); + } + } + }); + return !refs.isEmpty(); + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDictDuplicateKeysInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDictDuplicateKeysInspection.java index 3694318a..8e370400 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDictDuplicateKeysInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyDictDuplicateKeysInspection.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.*; import consulo.annotation.component.ExtensionImpl; import consulo.language.ast.ASTNode; @@ -24,119 +22,131 @@ 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; + import java.util.HashMap; import java.util.Map; /** - * User: catherine - *

* Inspection to detect using the same value as dictionary key twice. + * + * @author catherine */ @ExtensionImpl public class PyDictDuplicateKeysInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.duplicate.keys"); - } - - @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.inspNameDuplicateKeys(); } + @Nonnull @Override - public void visitPyDictLiteralExpression(PyDictLiteralExpression node) { - if (node.getElements().length != 0) { - final Map map = new HashMap(); - for (PyExpression exp : node.getElements()) { - final PyExpression key = ((PyKeyValueExpression)exp).getKey(); - if (key instanceof PyNumericLiteralExpression - || key instanceof PyStringLiteralExpression || key instanceof PyReferenceExpression) { - if (map.keySet().contains(key.getText())) { - registerProblem(key, "Dictionary contains duplicate keys " + key.getText()); - registerProblem(map.get(key.getText()), "Dictionary contains duplicate keys " + key.getText()); + 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 visitPyDictLiteralExpression(PyDictLiteralExpression node) { + if (node.getElements().length != 0) { + final Map map = new HashMap(); + for (PyExpression exp : node.getElements()) { + final PyExpression key = ((PyKeyValueExpression) exp).getKey(); + if (key instanceof PyNumericLiteralExpression + || key instanceof PyStringLiteralExpression || key instanceof PyReferenceExpression) { + if (map.keySet().contains(key.getText())) { + registerProblem(key, "Dictionary contains duplicate keys " + key.getText()); + registerProblem(map.get(key.getText()), "Dictionary contains duplicate keys " + key.getText()); + } + map.put(key.getText(), key); + } + } } - map.put(key.getText(), key); - } } - } - } - @Override - public void visitPyCallExpression(final PyCallExpression node) { - if (isDict(node)) { - final Map map = new HashMap(); - final PyArgumentList pyArgumentList = node.getArgumentList(); - if (pyArgumentList == null) return; - final PyExpression[] arguments = pyArgumentList.getArguments(); - for (PyExpression argument : arguments) { - if (argument instanceof PyParenthesizedExpression) - argument = ((PyParenthesizedExpression)argument).getContainedExpression(); - if (argument instanceof PySequenceExpression) { - for (PyElement el : ((PySequenceExpression)argument).getElements()) { - final PsiElement key = getKey(el); - checkKey(map, key); + @Override + public void visitPyCallExpression(final PyCallExpression node) { + if (isDict(node)) { + final Map map = new HashMap(); + final PyArgumentList pyArgumentList = node.getArgumentList(); + if (pyArgumentList == null) { + return; + } + final PyExpression[] arguments = pyArgumentList.getArguments(); + for (PyExpression argument : arguments) { + if (argument instanceof PyParenthesizedExpression) { + argument = ((PyParenthesizedExpression) argument).getContainedExpression(); + } + if (argument instanceof PySequenceExpression) { + for (PyElement el : ((PySequenceExpression) argument).getElements()) { + final PsiElement key = getKey(el); + checkKey(map, key); + } + } + else { + final PsiElement key = getKey(argument); + checkKey(map, key); + } + } } - } - else { - final PsiElement key = getKey(argument); - checkKey(map, key); - } } - } - } - private void checkKey(final Map map, final PsiElement node) { - if (node == null) return; - String key = node.getText(); - if (node instanceof PyStringLiteralExpression) - key = ((PyStringLiteralExpression)node).getStringValue(); - if (map.keySet().contains(key)) { - registerProblem(node, "Dictionary contains duplicate keys " + key); - registerProblem(map.get(key), "Dictionary contains duplicate keys " + key); - } - map.put(key, node); - } + private void checkKey(final Map map, final PsiElement node) { + if (node == null) { + return; + } + String key = node.getText(); + if (node instanceof PyStringLiteralExpression) { + key = ((PyStringLiteralExpression) node).getStringValue(); + } + if (map.keySet().contains(key)) { + registerProblem(node, "Dictionary contains duplicate keys " + key); + registerProblem(map.get(key), "Dictionary contains duplicate keys " + key); + } + map.put(key, node); + } - @Nullable - private static PsiElement getKey(final PyElement argument) { - if (argument instanceof PyParenthesizedExpression) { - final PyExpression expr = ((PyParenthesizedExpression)argument).getContainedExpression(); - if (expr instanceof PyTupleExpression) { - return ((PyTupleExpression)expr).getElements()[0]; + @Nullable + private static PsiElement getKey(final PyElement argument) { + if (argument instanceof PyParenthesizedExpression) { + final PyExpression expr = ((PyParenthesizedExpression) argument).getContainedExpression(); + if (expr instanceof PyTupleExpression) { + return ((PyTupleExpression) expr).getElements()[0]; + } + } + if (argument instanceof PyKeywordArgument) { + ASTNode keyWord = ((PyKeywordArgument) argument).getKeywordNode(); + if (keyWord != null) { + return keyWord.getPsi(); + } + } + return null; } - } - if (argument instanceof PyKeywordArgument) { - ASTNode keyWord = ((PyKeywordArgument)argument).getKeywordNode(); - if (keyWord != null) return keyWord.getPsi(); - } - return null; - } - private static boolean isDict(final PyCallExpression expression) { - final PyExpression callee = expression.getCallee(); - if (callee == null) return false; - final String name = callee.getText(); - if ("dict".equals(name)) { - return true; - } - return false; - } + private static boolean isDict(final PyCallExpression expression) { + final PyExpression callee = expression.getCallee(); + if (callee == null) { + return false; + } + final String name = callee.getText(); + if ("dict".equals(name)) { + return true; + } + return false; + } - } + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyExceptClausesOrderInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyExceptClausesOrderInspection.java index 4c301a61..f47f38d5 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyExceptClausesOrderInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyExceptClausesOrderInspection.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.inspections.quickfix.PyMoveExceptQuickFix; import com.jetbrains.python.psi.*; import consulo.annotation.component.ExtensionImpl; @@ -23,9 +22,10 @@ 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 java.util.HashSet; import java.util.Set; @@ -34,57 +34,59 @@ */ @ExtensionImpl public class PyExceptClausesOrderInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.bad.except.clauses.order"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameBadExceptClausesOrder(); + } - @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 visitPyTryExceptStatement(PyTryExceptStatement node) { - PyExceptPart[] exceptParts = node.getExceptParts(); - if (exceptParts.length > 1) { - Set exceptClasses = new HashSet<>(); - for (PyExceptPart exceptPart : exceptParts) { - PyExpression exceptClass = exceptPart.getExceptClass(); - if (exceptClass instanceof PyReferenceExpression) { - PsiElement element = ((PyReferenceExpression)exceptClass).followAssignmentsChain(getResolveContext()).getElement(); - if (element instanceof PyClass) { - PyClass pyClass = (PyClass)element; - if (exceptClasses.contains(pyClass)) { - registerProblem(exceptClass, PyBundle.message("INSP.class.$0.already.caught", pyClass.getName())); - } - else { - for (PyClass superClass : pyClass.getSuperClasses(null)) { - if (exceptClasses.contains(superClass)) { - registerProblem(exceptClass, - PyBundle.message("INSP.class.$0.superclass.$1.already.caught", superClass.getName(), pyClass.getName()), - new PyMoveExceptQuickFix - ()); - } + @Override + public void visitPyTryExceptStatement(PyTryExceptStatement node) { + PyExceptPart[] exceptParts = node.getExceptParts(); + if (exceptParts.length > 1) { + Set exceptClasses = new HashSet<>(); + for (PyExceptPart exceptPart : exceptParts) { + PyExpression exceptClass = exceptPart.getExceptClass(); + if (exceptClass instanceof PyReferenceExpression) { + PsiElement element = ((PyReferenceExpression) exceptClass).followAssignmentsChain(getResolveContext()).getElement(); + if (element instanceof PyClass) { + PyClass pyClass = (PyClass) element; + if (exceptClasses.contains(pyClass)) { + registerProblem(exceptClass, PyLocalize.inspClass$0AlreadyCaught(pyClass.getName()).get()); + } + else { + for (PyClass superClass : pyClass.getSuperClasses(null)) { + if (exceptClasses.contains(superClass)) { + registerProblem( + exceptClass, + PyLocalize.inspClass$0Superclass$1AlreadyCaught(superClass.getName(), pyClass.getName()).get(), + new PyMoveExceptQuickFix() + ); + } + } + } + exceptClasses.add(pyClass); + } + } } - } - exceptClasses.add(pyClass); } - } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyExceptionInheritInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyExceptionInheritInspection.java index f2fd1198..470c65e5 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyExceptionInheritInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyExceptionInheritInspection.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.inspections.quickfix.PyAddExceptionSuperClassQuickFix; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.types.PyClassLikeType; @@ -25,8 +24,8 @@ import consulo.language.psi.PsiElement; import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiPolyVariantReference; -import org.jetbrains.annotations.Nls; - +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -35,57 +34,62 @@ */ @ExtensionImpl public class PyExceptionInheritInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.exception.not.inherit"); - } - - @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.inspNameExceptionNotInherit(); } + @Nonnull @Override - public void visitPyRaiseStatement(PyRaiseStatement node) { - PyExpression[] expressions = node.getExpressions(); - if (expressions.length == 0) { - return; - } - PyExpression expression = expressions[0]; - if (expression instanceof PyCallExpression) { - PyExpression callee = ((PyCallExpression)expression).getCallee(); - if (callee instanceof PyReferenceExpression) { - final PsiPolyVariantReference reference = ((PyReferenceExpression)callee).getReference(getResolveContext()); - if (reference == null) { - return; - } - PsiElement psiElement = reference.resolve(); - if (psiElement instanceof PyClass) { - PyClass aClass = (PyClass)psiElement; - for (PyClassLikeType type : aClass.getAncestorTypes(myTypeEvalContext)) { - if (type == null) { - return; - } - final String name = type.getName(); - if (name == null || "BaseException".equals(name) || "Exception".equals(name)) { + 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) { return; - } } - registerProblem(expression, "Exception doesn't inherit from base \'Exception\' class", new PyAddExceptionSuperClassQuickFix()); - } + PyExpression expression = expressions[0]; + if (expression instanceof PyCallExpression) { + PyExpression callee = ((PyCallExpression) expression).getCallee(); + if (callee instanceof PyReferenceExpression) { + final PsiPolyVariantReference reference = ((PyReferenceExpression) callee).getReference(getResolveContext()); + if (reference == null) { + return; + } + PsiElement psiElement = reference.resolve(); + if (psiElement instanceof PyClass) { + PyClass aClass = (PyClass) psiElement; + for (PyClassLikeType type : aClass.getAncestorTypes(myTypeEvalContext)) { + if (type == null) { + return; + } + final String name = type.getName(); + if (name == null || "BaseException".equals(name) || "Exception".equals(name)) { + return; + } + } + registerProblem( + expression, + "Exception doesn't inherit from base \'Exception\' class", + new PyAddExceptionSuperClassQuickFix() + ); + } + } + } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyFromFutureImportInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyFromFutureImportInspection.java index 6e9ad00f..60c98a61 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyFromFutureImportInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyFromFutureImportInspection.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.PyNames; import com.jetbrains.python.impl.inspections.quickfix.MoveFromFutureImportQuickFix; import com.jetbrains.python.psi.*; @@ -25,10 +23,11 @@ import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiFile; -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.util.List; /** @@ -36,57 +35,59 @@ */ @ExtensionImpl public class PyFromFutureImportInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.from.future.import"); - } - - @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.inspNameFromFutureImport(); } + @Nonnull @Override - public void visitPyFromImportStatement(PyFromImportStatement node) { - PyReferenceExpression importSource = node.getImportSource(); - if (importSource != null && PyNames.FUTURE_MODULE.equals(importSource.getName())) { - PsiFile file = importSource.getContainingFile(); - if (file instanceof PyFile) { - final List statementList = ((PyFile)file).getStatements(); - boolean skippedDocString = false; - for (PyStatement statement : statementList) { - if (statement instanceof PyExpressionStatement && - ((PyExpressionStatement)statement).getExpression() instanceof PyStringLiteralExpression && - !skippedDocString) { - skippedDocString = true; - continue; - } - if (statement instanceof PyFromImportStatement) { - if (statement == node) { - return; - } - PyReferenceExpression source = ((PyFromImportStatement)statement).getImportSource(); - if (source != null && PyNames.FUTURE_MODULE.equals(source.getName())) { - continue; - } + 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 visitPyFromImportStatement(PyFromImportStatement node) { + PyReferenceExpression importSource = node.getImportSource(); + if (importSource != null && PyNames.FUTURE_MODULE.equals(importSource.getName())) { + PsiFile file = importSource.getContainingFile(); + if (file instanceof PyFile) { + final List statementList = ((PyFile) file).getStatements(); + boolean skippedDocString = false; + for (PyStatement statement : statementList) { + if (statement instanceof PyExpressionStatement && + ((PyExpressionStatement) statement).getExpression() instanceof PyStringLiteralExpression && + !skippedDocString) { + skippedDocString = true; + continue; + } + if (statement instanceof PyFromImportStatement) { + if (statement == node) { + return; + } + PyReferenceExpression source = ((PyFromImportStatement) statement).getImportSource(); + if (source != null && PyNames.FUTURE_MODULE.equals(source.getName())) { + continue; + } + } + registerProblem(node, "from __future__ imports must occur at the beginning of the file", + new MoveFromFutureImportQuickFix() + ); + return; + } + } } - registerProblem(node, "from __future__ imports must occur at the beginning of the file", - new MoveFromFutureImportQuickFix()); - return; - } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyGlobalUndefinedInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyGlobalUndefinedInspection.java index 9f55c9df..10fed28f 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyGlobalUndefinedInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyGlobalUndefinedInspection.java @@ -13,59 +13,58 @@ * 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.PyGlobalStatement; import com.jetbrains.python.psi.PyTargetExpression; 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; /** - * User: ktisha - *

* pylint W0601 + * + * @author ktisha */ @ExtensionImpl public class PyGlobalUndefinedInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.global.undefined"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameGlobalUndefined(); + } - @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 { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); - } + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } - @Override - public void visitPyGlobalStatement(PyGlobalStatement node) { - final PyTargetExpression[] globals = node.getGlobals(); + @Override + public void visitPyGlobalStatement(PyGlobalStatement node) { + final PyTargetExpression[] globals = node.getGlobals(); - for (PyTargetExpression global : globals) { - if (global.getReference().resolve() == global) { - registerProblem(global, PyBundle.message("INSP.NAME.global.$0.undefined", global.getName())); + for (PyTargetExpression global : globals) { + if (global.getReference().resolve() == global) { + registerProblem(global, PyLocalize.inspNameGlobal$0Undefined(global.getName()).get()); + } + } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyInconsistentIndentationInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyInconsistentIndentationInspection.java index 2b393d22..1dff5a9a 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyInconsistentIndentationInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyInconsistentIndentationInspection.java @@ -13,22 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.inspections; +import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.PythonLanguage; +import com.jetbrains.python.impl.inspections.quickfix.ConvertIndentsFix; +import com.jetbrains.python.impl.lexer.PythonIndentingLexer; import consulo.annotation.component.ExtensionImpl; -import consulo.language.editor.inspection.scheme.InspectionManager; +import consulo.document.util.TextRange; +import consulo.language.ast.IElementType; import consulo.language.editor.inspection.ProblemDescriptor; import consulo.language.editor.inspection.ProblemHighlightType; -import consulo.document.util.TextRange; +import consulo.language.editor.inspection.scheme.InspectionManager; import consulo.language.psi.PsiElement; import consulo.language.psi.PsiFile; -import consulo.language.ast.IElementType; -import com.jetbrains.python.PyTokenTypes; -import com.jetbrains.python.PythonLanguage; -import com.jetbrains.python.impl.inspections.quickfix.ConvertIndentsFix; -import com.jetbrains.python.impl.lexer.PythonIndentingLexer; -import org.jetbrains.annotations.Nls; +import consulo.localize.LocalizeValue; import jakarta.annotation.Nonnull; import java.util.ArrayList; @@ -39,98 +38,102 @@ */ @ExtensionImpl public class PyInconsistentIndentationInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return "Inconsistent indentation"; - } - - @Override - public ProblemDescriptor[] checkFile(@Nonnull PsiFile file, @Nonnull InspectionManager manager, boolean isOnTheFly) { - if (file.getLanguage() instanceof PythonLanguage) { - return new IndentValidator(file, manager, isOnTheFly).invoke(); - } - return ProblemDescriptor.EMPTY_ARRAY; - } - - private static class IndentValidator { - private PsiFile myFile; - private InspectionManager myManager; - private boolean myOnTheFly; - private List myProblems; - private int myLastTabs = 0; - private int myLastSpaces = 0; - - public IndentValidator(PsiFile file, InspectionManager manager, boolean isOnTheFly) { - myFile = file; - myManager = manager; - myOnTheFly = isOnTheFly; - myProblems = new ArrayList(); + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return LocalizeValue.localizeTODO("Inconsistent indentation"); } - public ProblemDescriptor[] invoke() { - PythonIndentingLexer lexer = new PythonIndentingLexer(); - final String text = myFile.getText(); - lexer.start(text); - while (lexer.getTokenType() != null) { - final IElementType tokenType = lexer.getTokenType(); - if (tokenType == PyTokenTypes.STATEMENT_BREAK) { - lexer.advance(); - while(lexer.getTokenType() != null && lexer.getTokenType() != PyTokenTypes.LINE_BREAK) { - lexer.advance(); - } - if (lexer.getTokenType() == PyTokenTypes.LINE_BREAK) { - String indent = text.substring(lexer.getTokenStart(), lexer.getTokenEnd()); - validateIndent(lexer.getTokenStart(), indent); - } + @Override + public ProblemDescriptor[] checkFile(@Nonnull PsiFile file, @Nonnull InspectionManager manager, boolean isOnTheFly) { + if (file.getLanguage() instanceof PythonLanguage) { + return new IndentValidator(file, manager, isOnTheFly).invoke(); } - lexer.advance(); - } - return myProblems.toArray(new ProblemDescriptor[myProblems.size()]); + return ProblemDescriptor.EMPTY_ARRAY; } - private void validateIndent(final int tokenStart, String indent) { - int lastLF = indent.lastIndexOf('\n'); - String lastLineIndent = indent.substring(lastLF+1); - int spaces = 0; - int tabs = 0; - final int length = lastLineIndent.length(); - for (int i = 0; i < length; i++) { - final char c = lastLineIndent.charAt(i); - if (c == ' ') spaces++; - else if (c == '\t') tabs++; - } - final int problemStart = tokenStart + lastLF + 1; - if (spaces > 0 && tabs > 0) { - reportProblem("Inconsistent indentation: mix of tabs and spaces", problemStart, length); - // don't know which one is correct => don't complain about inconsistent indentation on subsequent lines which use - // either tabs or spaces - myLastSpaces = 0; - myLastTabs = 0; - } - else { - if (spaces > 0 && myLastTabs > 0) { - reportProblem("Inconsistent indentation: previous line used tabs, this line uses spaces", problemStart, length); + private static class IndentValidator { + private PsiFile myFile; + private InspectionManager myManager; + private boolean myOnTheFly; + private List myProblems; + private int myLastTabs = 0; + private int myLastSpaces = 0; + + public IndentValidator(PsiFile file, InspectionManager manager, boolean isOnTheFly) { + myFile = file; + myManager = manager; + myOnTheFly = isOnTheFly; + myProblems = new ArrayList(); } - else if (tabs > 0 && myLastSpaces > 0) { - reportProblem("Inconsistent indentation: previous line used spaces, this line uses tabs", problemStart, length); + + public ProblemDescriptor[] invoke() { + PythonIndentingLexer lexer = new PythonIndentingLexer(); + final String text = myFile.getText(); + lexer.start(text); + while (lexer.getTokenType() != null) { + final IElementType tokenType = lexer.getTokenType(); + if (tokenType == PyTokenTypes.STATEMENT_BREAK) { + lexer.advance(); + while (lexer.getTokenType() != null && lexer.getTokenType() != PyTokenTypes.LINE_BREAK) { + lexer.advance(); + } + if (lexer.getTokenType() == PyTokenTypes.LINE_BREAK) { + String indent = text.substring(lexer.getTokenStart(), lexer.getTokenEnd()); + validateIndent(lexer.getTokenStart(), indent); + } + } + lexer.advance(); + } + return myProblems.toArray(new ProblemDescriptor[myProblems.size()]); } - if (spaces > 0 || tabs > 0) { - myLastTabs = tabs; - myLastSpaces = spaces; + + private void validateIndent(final int tokenStart, String indent) { + int lastLF = indent.lastIndexOf('\n'); + String lastLineIndent = indent.substring(lastLF + 1); + int spaces = 0; + int tabs = 0; + final int length = lastLineIndent.length(); + for (int i = 0; i < length; i++) { + final char c = lastLineIndent.charAt(i); + if (c == ' ') { + spaces++; + } + else if (c == '\t') { + tabs++; + } + } + final int problemStart = tokenStart + lastLF + 1; + if (spaces > 0 && tabs > 0) { + reportProblem("Inconsistent indentation: mix of tabs and spaces", problemStart, length); + // don't know which one is correct => don't complain about inconsistent indentation on subsequent lines which use + // either tabs or spaces + myLastSpaces = 0; + myLastTabs = 0; + } + else { + if (spaces > 0 && myLastTabs > 0) { + reportProblem("Inconsistent indentation: previous line used tabs, this line uses spaces", problemStart, length); + } + else if (tabs > 0 && myLastSpaces > 0) { + reportProblem("Inconsistent indentation: previous line used spaces, this line uses tabs", problemStart, length); + } + if (spaces > 0 || tabs > 0) { + myLastTabs = tabs; + myLastSpaces = spaces; + } + } } - } - } - private void reportProblem(final String descriptionTemplate, final int problemStart, final int problemLength) { - PsiElement elt = myFile.findElementAt(problemStart); - int startOffset = problemStart - elt.getTextRange().getStartOffset(); - int endOffset = startOffset + problemLength; - myProblems.add(myManager.createProblemDescriptor(elt, new TextRange(startOffset, endOffset), - descriptionTemplate, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myOnTheFly, - new ConvertIndentsFix(false), new ConvertIndentsFix(true))); + private void reportProblem(final String descriptionTemplate, final int problemStart, final int problemLength) { + PsiElement elt = myFile.findElementAt(problemStart); + int startOffset = problemStart - elt.getTextRange().getStartOffset(); + int endOffset = startOffset + problemLength; + myProblems.add(myManager.createProblemDescriptor(elt, new TextRange(startOffset, endOffset), + descriptionTemplate, + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, myOnTheFly, + new ConvertIndentsFix(false), new ConvertIndentsFix(true) + )); + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyIncorrectDocstringInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyIncorrectDocstringInspection.java index 7d2404d9..ba4111f0 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyIncorrectDocstringInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyIncorrectDocstringInspection.java @@ -17,7 +17,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.documentation.docstrings.DocStringUtil; import com.jetbrains.python.impl.documentation.docstrings.PlainDocString; import com.jetbrains.python.impl.inspections.quickfix.DocstringQuickFix; @@ -26,8 +25,10 @@ import consulo.annotation.component.ExtensionImpl; import consulo.language.editor.inspection.LocalInspectionToolSession; import consulo.language.editor.inspection.ProblemsHolder; - +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -41,96 +42,100 @@ */ @ExtensionImpl public class PyIncorrectDocstringInspection extends PyBaseDocstringInspection { - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.incorrect.docstring"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameIncorrectDocstring(); + } - @Nonnull - @Override - public Visitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session) { + @Nonnull + @Override + public Visitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session) { - @Override - protected void checkDocString(@Nonnull PyDocStringOwner node) { - final PyStringLiteralExpression docstringExpr = node.getDocStringExpression(); - if (docstringExpr != null) { - checkParameters(node, docstringExpr); - } - } + @Override + protected void checkDocString(@Nonnull PyDocStringOwner node) { + final PyStringLiteralExpression docstringExpr = node.getDocStringExpression(); + if (docstringExpr != null) { + checkParameters(node, docstringExpr); + } + } - private void checkParameters(@Nonnull PyDocStringOwner pyDocStringOwner, @Nonnull PyStringLiteralExpression node) { - final StructuredDocString docString = DocStringUtil.parseDocString(node); - if (docString instanceof PlainDocString) { - return; - } + private void checkParameters(@Nonnull PyDocStringOwner pyDocStringOwner, @Nonnull PyStringLiteralExpression node) { + final StructuredDocString docString = DocStringUtil.parseDocString(node); + if (docString instanceof PlainDocString) { + return; + } - if (pyDocStringOwner instanceof PyFunction) { - final PyParameter[] realParams = ((PyFunction)pyDocStringOwner).getParameterList().getParameters(); + if (pyDocStringOwner instanceof PyFunction) { + final PyParameter[] realParams = ((PyFunction) pyDocStringOwner).getParameterList().getParameters(); - final List missingParams = getMissingParams(docString, realParams); - if (!missingParams.isEmpty()) { - for (PyNamedParameter param : missingParams) { - registerProblem(param, - PyBundle.message("INSP.missing.parameter.in.docstring", param.getName()), - new DocstringQuickFix(param, null)); - } - } - final List unexpectedParams = getUnexpectedParams(docString, realParams); - if (!unexpectedParams.isEmpty()) { - for (Substring param : unexpectedParams) { - final ProblemsHolder holder = getHolder(); + final List missingParams = getMissingParams(docString, realParams); + if (!missingParams.isEmpty()) { + for (PyNamedParameter param : missingParams) { + registerProblem( + param, + PyLocalize.inspMissingParameterInDocstring(param.getName()).get(), + new DocstringQuickFix(param, null) + ); + } + } + final List unexpectedParams = getUnexpectedParams(docString, realParams); + if (!unexpectedParams.isEmpty()) { + for (Substring param : unexpectedParams) { + final ProblemsHolder holder = getHolder(); - if (holder != null) { - holder.registerProblem(node, - param.getTextRange(), - PyBundle.message("INSP.unexpected.parameter.in.docstring", param), - new DocstringQuickFix(null, param.getValue())); - } + if (holder != null) { + holder.newProblem(PyLocalize.inspUnexpectedParameterInDocstring(param)) + .range(node, param.getTextRange()) + .withFix(new DocstringQuickFix(null, param.getValue())) + .create(); + } + } + } + } } - } - } - } - }; - } - - @Nonnull - private static List getMissingParams(@Nonnull StructuredDocString docString, @Nonnull PyParameter[] realParams) { - final List missing = new ArrayList<>(); - final List docStringParameters = docString.getParameters(); - if (docStringParameters.isEmpty()) { - return Collections.emptyList(); + }; } - for (PyParameter p : realParams) { - final PyNamedParameter named = as(p, PyNamedParameter.class); - if (p.isSelf() || named == null || named.isPositionalContainer() || named.isKeywordContainer()) { - continue; - } - if (!docStringParameters.contains(p.getName())) { - missing.add((PyNamedParameter)p); - } + @Nonnull + private static List getMissingParams(@Nonnull StructuredDocString docString, @Nonnull PyParameter[] realParams) { + final List missing = new ArrayList<>(); + final List docStringParameters = docString.getParameters(); + if (docStringParameters.isEmpty()) { + return Collections.emptyList(); + } + + for (PyParameter p : realParams) { + final PyNamedParameter named = as(p, PyNamedParameter.class); + if (p.isSelf() || named == null || named.isPositionalContainer() || named.isKeywordContainer()) { + continue; + } + if (!docStringParameters.contains(p.getName())) { + missing.add((PyNamedParameter) p); + } + } + return missing; } - return missing; - } - @Nonnull - private static List getUnexpectedParams(@Nonnull StructuredDocString docString, @Nonnull PyParameter[] realParams) { - final Map unexpected = Maps.newHashMap(); + @Nonnull + private static List getUnexpectedParams(@Nonnull StructuredDocString docString, @Nonnull PyParameter[] realParams) { + final Map unexpected = Maps.newHashMap(); - for (Substring s : docString.getParameterSubstrings()) { - unexpected.put(s.toString(), s); - } + for (Substring s : docString.getParameterSubstrings()) { + unexpected.put(s.toString(), s); + } - for (PyParameter p : realParams) { - if (unexpected.containsKey(p.getName())) { - unexpected.remove(p.getName()); - } + for (PyParameter p : realParams) { + if (unexpected.containsKey(p.getName())) { + unexpected.remove(p.getName()); + } + } + return Lists.newArrayList(unexpected.values()); } - return Lists.newArrayList(unexpected.values()); - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyInitNewSignatureInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyInitNewSignatureInspection.java index d4303fd8..c5e9cd46 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyInitNewSignatureInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyInitNewSignatureInspection.java @@ -15,19 +15,18 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.impl.inspections.quickfix.PyChangeSignatureQuickFix; +import com.jetbrains.python.impl.psi.PyUtil; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFunction; -import com.jetbrains.python.impl.psi.PyUtil; import consulo.annotation.component.ExtensionImpl; 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.lang.ObjectUtil; -import org.jetbrains.annotations.Nls; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -38,52 +37,57 @@ */ @ExtensionImpl public class PyInitNewSignatureInspection extends PyInspection { - @Nls - @Nonnull - public String getDisplayName() { - return PyBundle.message("INSP.NAME.new.init.signature"); - } - - @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.inspNameNewInitSignature(); } + @Nonnull @Override - public void visitPyFunction(PyFunction node) { - final String functionName = node.getName(); - if (!PyNames.NEW.equals(functionName) && !PyNames.INIT.equals(functionName)) { - return; - } - final PyClass cls = node.getContainingClass(); - if (cls == null) { - return; - } - if (!cls.isNewStyleClass(null)) { - return; - } - final String complementaryName = PyNames.NEW.equals(functionName) ? PyNames.INIT : PyNames.NEW; - final PyFunction complementaryMethod = cls.findMethodByName(complementaryName, true, null); - if (complementaryMethod == null || PyUtil.isObjectClass(ObjectUtil.assertNotNull(complementaryMethod.getContainingClass()))) { - return; - } - if (!PyUtil.isSignatureCompatibleTo(complementaryMethod, node, myTypeEvalContext) && - !PyUtil.isSignatureCompatibleTo(node, complementaryMethod, myTypeEvalContext) && - node.getContainingFile() == cls.getContainingFile()) { - registerProblem(node.getParameterList(), - PyNames.NEW.equals(node.getName()) ? PyBundle.message("INSP.new.incompatible.to.init") : PyBundle.message( - "INSP.init.incompatible.to.new"), - new PyChangeSignatureQuickFix(false)); - } + 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 visitPyFunction(PyFunction node) { + final String functionName = node.getName(); + if (!PyNames.NEW.equals(functionName) && !PyNames.INIT.equals(functionName)) { + return; + } + final PyClass cls = node.getContainingClass(); + if (cls == null) { + return; + } + if (!cls.isNewStyleClass(null)) { + return; + } + final String complementaryName = PyNames.NEW.equals(functionName) ? PyNames.INIT : PyNames.NEW; + final PyFunction complementaryMethod = cls.findMethodByName(complementaryName, true, null); + if (complementaryMethod == null || PyUtil.isObjectClass(ObjectUtil.assertNotNull(complementaryMethod.getContainingClass()))) { + return; + } + if (!PyUtil.isSignatureCompatibleTo(complementaryMethod, node, myTypeEvalContext) && + !PyUtil.isSignatureCompatibleTo(node, complementaryMethod, myTypeEvalContext) && + node.getContainingFile() == cls.getContainingFile()) { + registerProblem( + node.getParameterList(), + PyNames.NEW.equals(node.getName()) + ? PyLocalize.inspNewIncompatibleToInit().get() + : PyLocalize.inspInitIncompatibleToNew().get(), + new PyChangeSignatureQuickFix(false) + ); + } + } } - } } \ No newline at end of file diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyListCreationInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyListCreationInspection.java index b5166a71..2debc543 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyListCreationInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyListCreationInspection.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.ListCreationQuickFix; import com.jetbrains.python.psi.*; import consulo.annotation.component.ExtensionImpl; @@ -24,88 +22,99 @@ 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 + * @author catherine */ @ExtensionImpl public class PyListCreationInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.list.creation"); - } - - @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.inspNameListCreation(); } + @Nonnull @Override - public void visitPyAssignmentStatement(PyAssignmentStatement node) { - if (!(node.getAssignedValue() instanceof PyListLiteralExpression)) return; - final PyExpression[] targets = node.getTargets(); - if (targets.length != 1) return; - final PyExpression target = targets[0]; - final String name = target.getName(); - if (name == null) return; + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); + } - PyStatement expressionStatement = PsiTreeUtil.getNextSiblingOfType(node, PyStatement.class); - if (!(expressionStatement instanceof PyExpressionStatement)) - return; + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } - ListCreationQuickFix quickFix = null; + @Override + public void visitPyAssignmentStatement(PyAssignmentStatement node) { + if (!(node.getAssignedValue() instanceof PyListLiteralExpression)) { + return; + } + final PyExpression[] targets = node.getTargets(); + if (targets.length != 1) { + return; + } + final PyExpression target = targets[0]; + final String name = target.getName(); + if (name == null) { + return; + } - final String message = "This list creation could be rewritten as a list literal"; - while (expressionStatement instanceof PyExpressionStatement) { - final PyExpression statement = ((PyExpressionStatement)expressionStatement).getExpression(); - if (!(statement instanceof PyCallExpression)) break; + PyStatement expressionStatement = PsiTreeUtil.getNextSiblingOfType(node, PyStatement.class); + if (!(expressionStatement instanceof PyExpressionStatement)) { + return; + } + + ListCreationQuickFix quickFix = null; - final PyCallExpression callExpression = (PyCallExpression)statement; - final PyExpression callee = callExpression.getCallee(); - if (callee instanceof PyQualifiedExpression) { - final PyExpression qualifier = ((PyQualifiedExpression)callee).getQualifier(); - final String funcName = ((PyQualifiedExpression)callee).getReferencedName(); - if (qualifier != null && name.equals(qualifier.getText()) && "append".equals(funcName)) { - final PyArgumentList argList = callExpression.getArgumentList(); - if (argList != null) { - for (PyExpression argument : argList.getArguments()) { - if (argument.getText().equals(name)) { - if (quickFix != null) - registerProblem(node, message, quickFix); - return; + final String message = "This list creation could be rewritten as a list literal"; + while (expressionStatement instanceof PyExpressionStatement) { + final PyExpression statement = ((PyExpressionStatement) expressionStatement).getExpression(); + if (!(statement instanceof PyCallExpression)) { + break; + } + + final PyCallExpression callExpression = (PyCallExpression) statement; + final PyExpression callee = callExpression.getCallee(); + if (callee instanceof PyQualifiedExpression) { + final PyExpression qualifier = ((PyQualifiedExpression) callee).getQualifier(); + final String funcName = ((PyQualifiedExpression) callee).getReferencedName(); + if (qualifier != null && name.equals(qualifier.getText()) && "append".equals(funcName)) { + final PyArgumentList argList = callExpression.getArgumentList(); + if (argList != null) { + for (PyExpression argument : argList.getArguments()) { + if (argument.getText().equals(name)) { + if (quickFix != null) { + registerProblem(node, message, quickFix); + } + return; + } + } + if (quickFix == null) { + quickFix = new ListCreationQuickFix(node); + } + quickFix.addStatement((PyExpressionStatement) expressionStatement); + } + } + } + if (quickFix == null) { + return; } - } - if (quickFix == null) { - quickFix = new ListCreationQuickFix(node); - } - quickFix.addStatement((PyExpressionStatement)expressionStatement); + expressionStatement = PsiTreeUtil.getNextSiblingOfType(expressionStatement, PyStatement.class); } - } - } - if (quickFix == null) { - return; - } - expressionStatement = PsiTreeUtil.getNextSiblingOfType(expressionStatement, PyStatement.class); - } - if (quickFix != null) { - registerProblem(node, message, quickFix); - } + if (quickFix != null) { + registerProblem(node, message, quickFix); + } + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMandatoryEncodingInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMandatoryEncodingInspection.java index 6bf4713e..22f4b39b 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMandatoryEncodingInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMandatoryEncodingInspection.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.PythonFileType; import com.jetbrains.python.impl.inspections.quickfix.AddEncodingQuickFix; import com.jetbrains.python.psi.PyFile; @@ -26,71 +24,71 @@ 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; /** - * User : catherine + * @author catherine */ @ExtensionImpl -public class PyMandatoryEncodingInspection extends PyInspection -{ - @Nonnull - @Override - public InspectionToolState createStateProvider() - { - return new PyMandatoryEncodingInspectionState(); - } +public class PyMandatoryEncodingInspection extends PyInspection { + @Nonnull + @Override + public InspectionToolState createStateProvider() { + return new PyMandatoryEncodingInspectionState(); + } - @Nls - @Nonnull - @Override - public String getDisplayName() - { - return PyBundle.message("INSP.NAME.mandatory.encoding"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameMandatoryEncoding(); + } - @Override - public boolean isEnabledByDefault() - { - return true; - } + @Override + public boolean isEnabledByDefault() { + return true; + } - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) - { - return new Visitor(holder, session, (PyMandatoryEncodingInspectionState) state); - } + @Nonnull + @Override + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session, (PyMandatoryEncodingInspectionState) state); + } - private class Visitor extends PyInspectionVisitor - { - private final PyMandatoryEncodingInspectionState myState; + private class Visitor extends PyInspectionVisitor { + private final PyMandatoryEncodingInspectionState myState; - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session, PyMandatoryEncodingInspectionState state) - { - super(holder, session); - myState = state; - } + public Visitor( + @Nullable ProblemsHolder holder, + @Nonnull LocalInspectionToolSession session, + PyMandatoryEncodingInspectionState state + ) { + super(holder, session); + myState = state; + } - @Override - public void visitPyFile(PyFile node) - { - final String charsetString = PythonFileType.getCharsetFromEncodingDeclaration(node.getText()); - if(charsetString == null) - { - TextRange tr = new TextRange(0, 0); - ProblemsHolder holder = getHolder(); - if(holder != null) - { - holder.registerProblem(node, tr, "No encoding specified for file", new AddEncodingQuickFix(myState.myDefaultEncoding,myState.myEncodingFormatIndex)); - } - } - } - } + @Override + public void visitPyFile(PyFile node) { + final String charsetString = PythonFileType.getCharsetFromEncodingDeclaration(node.getText()); + if (charsetString == null) { + TextRange tr = new TextRange(0, 0); + ProblemsHolder holder = getHolder(); + if (holder != null) { + holder.registerProblem( + node, + tr, + "No encoding specified for file", + new AddEncodingQuickFix(myState.myDefaultEncoding, myState.myEncodingFormatIndex) + ); + } + } + } + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodFirstArgAssignmentInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodFirstArgAssignmentInspection.java index e6fb6f4e..9a61db12 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodFirstArgAssignmentInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodFirstArgAssignmentInspection.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.psi.PyUtil; import com.jetbrains.python.psi.*; @@ -25,10 +24,11 @@ import consulo.language.psi.PsiElement; import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiNamedElement; -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.util.List; /** @@ -38,108 +38,110 @@ */ @ExtensionImpl public class PyMethodFirstArgAssignmentInspection extends PyInspection { - @Nls - @Nonnull - public String getDisplayName() { - return PyBundle.message("INSP.NAME.first.arg.assign"); - } - - @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.inspNameFirstArgAssign(); } - private void complain(PsiElement element, String name) { - registerProblem(element, PyBundle.message("INSP.first.arg.$0.assigned", name)); + @Nonnull + @Override + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } - private void handleTarget(PyQualifiedExpression target, String name) { - if (!target.isQualified() && name.equals(target.getText())) { - complain(target, name); - } - } + public static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } - @Nullable - private static String extractFirstParamName(PyElement node) { - // are we a method? - List place = PyUtil.searchForWrappingMethod(node, true); - if (place == null || place.size() < 2) { - return null; - } - PyFunction method = (PyFunction)place.get(place.size() - 2); - //PyClass owner = (PyClass)place.get(place.size()-1); - // what is our first param? - PyParameter[] params = method.getParameterList().getParameters(); - if (params.length < 1) { - return null; // no params - } - PyNamedParameter first_parm = params[0].getAsNamed(); - if (first_parm == null) { - return null; - } - if (first_parm.isKeywordContainer() || first_parm.isPositionalContainer()) { - return null; // legal but crazy cases; back off - } - final String first_param_name = first_parm.getName(); - if (first_param_name == null || first_param_name.length() < 1) { - return null; // ignore cases of incorrect code - } - // is it a static method? - PyFunction.Modifier modifier = method.getModifier(); - if (modifier == PyFunction.Modifier.STATICMETHOD) { - return null; // these may do whatever they please - } - return first_param_name; - } + private void complain(PsiElement element, String name) { + registerProblem(element, PyLocalize.inspFirstArg$0Assigned(name).get()); + } - private void markNameDefiner(PyNamedElementContainer definer) { - final String first_param_name = extractFirstParamName((PyElement)definer); - if (first_param_name != null) { - // check the targets - for (PsiNamedElement elt : definer.getNamedElements()) { - if (elt instanceof PyTargetExpression) { - handleTarget((PyTargetExpression)elt, first_param_name); - } + private void handleTarget(PyQualifiedExpression target, String name) { + if (!target.isQualified() && name.equals(target.getText())) { + complain(target, name); + } } - } - } - @Override - public void visitPyAssignmentStatement(PyAssignmentStatement node) { - markNameDefiner(node); - } + @Nullable + private static String extractFirstParamName(PyElement node) { + // are we a method? + List place = PyUtil.searchForWrappingMethod(node, true); + if (place == null || place.size() < 2) { + return null; + } + PyFunction method = (PyFunction) place.get(place.size() - 2); + //PyClass owner = (PyClass)place.get(place.size()-1); + // what is our first param? + PyParameter[] params = method.getParameterList().getParameters(); + if (params.length < 1) { + return null; // no params + } + PyNamedParameter first_parm = params[0].getAsNamed(); + if (first_parm == null) { + return null; + } + if (first_parm.isKeywordContainer() || first_parm.isPositionalContainer()) { + return null; // legal but crazy cases; back off + } + final String first_param_name = first_parm.getName(); + if (first_param_name == null || first_param_name.length() < 1) { + return null; // ignore cases of incorrect code + } + // is it a static method? + PyFunction.Modifier modifier = method.getModifier(); + if (modifier == PyFunction.Modifier.STATICMETHOD) { + return null; // these may do whatever they please + } + return first_param_name; + } - @Override - public void visitPyAugAssignmentStatement(PyAugAssignmentStatement node) { - final String first_param_name = extractFirstParamName(node); - if (first_param_name != null) { - PyExpression target = node.getTarget(); - if (target instanceof PyQualifiedExpression) { - handleTarget((PyQualifiedExpression)target, first_param_name); + private void markNameDefiner(PyNamedElementContainer definer) { + final String first_param_name = extractFirstParamName((PyElement) definer); + if (first_param_name != null) { + // check the targets + for (PsiNamedElement elt : definer.getNamedElements()) { + if (elt instanceof PyTargetExpression) { + handleTarget((PyTargetExpression) elt, first_param_name); + } + } + } + } + + @Override + public void visitPyAssignmentStatement(PyAssignmentStatement node) { + markNameDefiner(node); } - else if (target instanceof PyTupleExpression) { - for (PyExpression elt : PyUtil.flattenedParensAndTuples(((PyTupleExpression)target).getElements())) { - if (elt instanceof PyQualifiedExpression) { - handleTarget((PyQualifiedExpression)elt, first_param_name); + + @Override + public void visitPyAugAssignmentStatement(PyAugAssignmentStatement node) { + final String first_param_name = extractFirstParamName(node); + if (first_param_name != null) { + PyExpression target = node.getTarget(); + if (target instanceof PyQualifiedExpression) { + handleTarget((PyQualifiedExpression) target, first_param_name); + } + else if (target instanceof PyTupleExpression) { + for (PyExpression elt : PyUtil.flattenedParensAndTuples(((PyTupleExpression) target).getElements())) { + if (elt instanceof PyQualifiedExpression) { + handleTarget((PyQualifiedExpression) elt, first_param_name); + } + } + } } - } } - } - } - @Override - public void visitPyForStatement(PyForStatement node) { - markNameDefiner(node); - } + @Override + public void visitPyForStatement(PyForStatement node) { + markNameDefiner(node); + } /* TODO: implement when WithStmt is part-ified @Override @@ -148,23 +150,25 @@ public void visitPyWithStatement(PyWithStatement node) { } */ - private void markDefinition(PyElement definer) { - final String first_param_name = extractFirstParamName(definer); - if (first_param_name != null && first_param_name.equals(definer.getName())) { - complain(definer.getNode().findChildByType(PyTokenTypes.IDENTIFIER).getPsi(), - first_param_name); // no NPE here, or we won't have the name - } - } + private void markDefinition(PyElement definer) { + final String first_param_name = extractFirstParamName(definer); + if (first_param_name != null && first_param_name.equals(definer.getName())) { + complain( + definer.getNode().findChildByType(PyTokenTypes.IDENTIFIER).getPsi(), + first_param_name + ); // no NPE here, or we won't have the name + } + } - @Override - public void visitPyFunction(PyFunction definer) { - markDefinition(definer); - } + @Override + public void visitPyFunction(PyFunction definer) { + markDefinition(definer); + } - @Override - public void visitPyClass(PyClass definer) { - markDefinition(definer); - } + @Override + public void visitPyClass(PyClass definer) { + markDefinition(definer); + } - } + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodMayBeStaticInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodMayBeStaticInspection.java index b63e9fa3..4284945f 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodMayBeStaticInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodMayBeStaticInspection.java @@ -15,164 +15,166 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.impl.inspections.quickfix.PyMakeFunctionFromMethodQuickFix; import com.jetbrains.python.impl.inspections.quickfix.PyMakeMethodStaticQuickFix; -import com.jetbrains.python.psi.*; import com.jetbrains.python.impl.psi.search.PyOverridingMethodsSearch; import com.jetbrains.python.impl.psi.search.PySuperMethodsSearch; +import com.jetbrains.python.psi.*; 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.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; + import java.util.List; import java.util.Locale; /** - * User: ktisha + * @author ktisha */ @ExtensionImpl public class PyMethodMayBeStaticInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.method.may.be.static"); - } - - @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.inspNameMethodMayBeStatic(); } + @Nonnull @Override - public void visitPyFunction(PyFunction node) { - if (PyNames.getBuiltinMethods(LanguageLevel.forElement(node)).containsKey(node.getName())) { - return; - } - final PyClass containingClass = node.getContainingClass(); - if (containingClass == null) { - return; - } - final PsiElement firstSuper = PySuperMethodsSearch.search(node, myTypeEvalContext).findFirst(); - if (firstSuper != null) { - return; - } - final PyFunction firstOverride = PyOverridingMethodsSearch.search(node, true).findFirst(); - if (firstOverride != null) { - return; - } - final PyDecoratorList decoratorList = node.getDecoratorList(); - if (decoratorList != null) { - return; - } - if (node.getModifier() != null) { - return; - } - final Property property = containingClass.findPropertyByCallable(node); - if (property != null) { - return; - } - final List attributes = node.findAttributes(); - if (!attributes.isEmpty()) { - return; - } - if (isTestElement(node)) { - return; - } - - final PyStatementList statementList = node.getStatementList(); - final PyStatement[] statements = statementList.getStatements(); - - if (statements.length == 1 && statements[0] instanceof PyPassStatement) { - return; - } - - final PyParameter[] parameters = node.getParameterList().getParameters(); - - final String selfName; - if (parameters.length > 0) { - final String name = parameters[0].getName(); - selfName = name != null ? name : parameters[0].getText(); - } - else { - selfName = PyNames.CANONICAL_SELF; - } - - final boolean[] mayBeStatic = {true}; - PyRecursiveElementVisitor visitor = new PyRecursiveElementVisitor() { + 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) { - super.visitPyRaiseStatement(node); - final PyExpression[] expressions = node.getExpressions(); - if (expressions.length == 1) { - final PyExpression expression = expressions[0]; - if (expression instanceof PyCallExpression) { - final PyExpression callee = ((PyCallExpression)expression).getCallee(); - if (callee != null && PyNames.NOT_IMPLEMENTED_ERROR.equals(callee.getText())) { - mayBeStatic[0] = false; - } + public void visitPyFunction(PyFunction node) { + if (PyNames.getBuiltinMethods(LanguageLevel.forElement(node)).containsKey(node.getName())) { + return; } - else if (PyNames.NOT_IMPLEMENTED_ERROR.equals(expression.getText())) { - mayBeStatic[0] = false; + final PyClass containingClass = node.getContainingClass(); + if (containingClass == null) { + return; + } + final PsiElement firstSuper = PySuperMethodsSearch.search(node, myTypeEvalContext).findFirst(); + if (firstSuper != null) { + return; + } + final PyFunction firstOverride = PyOverridingMethodsSearch.search(node, true).findFirst(); + if (firstOverride != null) { + return; + } + final PyDecoratorList decoratorList = node.getDecoratorList(); + if (decoratorList != null) { + return; + } + if (node.getModifier() != null) { + return; + } + final Property property = containingClass.findPropertyByCallable(node); + if (property != null) { + return; + } + final List attributes = node.findAttributes(); + if (!attributes.isEmpty()) { + return; + } + if (isTestElement(node)) { + return; } - } - } - @Override - public void visitPyReferenceExpression(PyReferenceExpression node) { - super.visitPyReferenceExpression(node); - if (selfName.equals(node.getName())) { - mayBeStatic[0] = false; - } - } + final PyStatementList statementList = node.getStatementList(); + final PyStatement[] statements = statementList.getStatements(); - @Override - public void visitPyCallExpression(PyCallExpression node) { - super.visitPyCallExpression(node); - if (LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON30) && node.isCalleeText(PyNames.SUPER)) { - mayBeStatic[0] = false; - } + if (statements.length == 1 && statements[0] instanceof PyPassStatement) { + return; + } + + final PyParameter[] parameters = node.getParameterList().getParameters(); + + final String selfName; + if (parameters.length > 0) { + final String name = parameters[0].getName(); + selfName = name != null ? name : parameters[0].getText(); + } + else { + selfName = PyNames.CANONICAL_SELF; + } + + final boolean[] mayBeStatic = {true}; + PyRecursiveElementVisitor visitor = new PyRecursiveElementVisitor() { + @Override + public void visitPyRaiseStatement(PyRaiseStatement node) { + super.visitPyRaiseStatement(node); + final PyExpression[] expressions = node.getExpressions(); + if (expressions.length == 1) { + final PyExpression expression = expressions[0]; + if (expression instanceof PyCallExpression) { + final PyExpression callee = ((PyCallExpression) expression).getCallee(); + if (callee != null && PyNames.NOT_IMPLEMENTED_ERROR.equals(callee.getText())) { + mayBeStatic[0] = false; + } + } + else if (PyNames.NOT_IMPLEMENTED_ERROR.equals(expression.getText())) { + mayBeStatic[0] = false; + } + } + } + + @Override + public void visitPyReferenceExpression(PyReferenceExpression node) { + super.visitPyReferenceExpression(node); + if (selfName.equals(node.getName())) { + mayBeStatic[0] = false; + } + } + + @Override + public void visitPyCallExpression(PyCallExpression node) { + super.visitPyCallExpression(node); + if (LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON30) && node.isCalleeText(PyNames.SUPER)) { + mayBeStatic[0] = false; + } + } + }; + node.accept(visitor); + final PsiElement identifier = node.getNameIdentifier(); + if (mayBeStatic[0] && identifier != null) { + registerProblem( + identifier, + PyLocalize.inspMethodMayBeStatic().get(), + ProblemHighlightType.WEAK_WARNING, + null, + new PyMakeMethodStaticQuickFix(), + new PyMakeFunctionFromMethodQuickFix() + ); + } } - }; - node.accept(visitor); - final PsiElement identifier = node.getNameIdentifier(); - if (mayBeStatic[0] && identifier != null) { - registerProblem(identifier, - PyBundle.message("INSP.method.may.be.static"), - ProblemHighlightType.WEAK_WARNING, - null, - new PyMakeMethodStaticQuickFix(), - new - PyMakeFunctionFromMethodQuickFix()); - } } - } - private static boolean isTestElement(@Nonnull PyFunction node) { - final String methodName = node.getName(); - final PyClass pyClass = node.getContainingClass(); - final String className = pyClass == null ? null : pyClass.getName(); + private static boolean isTestElement(@Nonnull PyFunction node) { + final String methodName = node.getName(); + final PyClass pyClass = node.getContainingClass(); + final String className = pyClass == null ? null : pyClass.getName(); - return methodName != null && className != null && methodName.toLowerCase(Locale.getDefault()) - .startsWith("test") && className.toLowerCase(Locale.getDefault()) - .startsWith("test"); - } + return methodName != null && className != null && methodName.toLowerCase(Locale.getDefault()) + .startsWith("test") && className.toLowerCase(Locale.getDefault()) + .startsWith("test"); + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodOverridingInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodOverridingInspection.java index 94070756..49b566b8 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodOverridingInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodOverridingInspection.java @@ -15,20 +15,19 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.impl.inspections.quickfix.PyChangeSignatureQuickFix; -import com.jetbrains.python.psi.PyClass; -import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.impl.psi.PyUtil; import com.jetbrains.python.impl.psi.search.PySuperMethodsSearch; +import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyFunction; 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; import jakarta.annotation.Nullable; @@ -37,49 +36,53 @@ */ @ExtensionImpl public class PyMethodOverridingInspection extends PyInspection { - @Nls - @Nonnull - public String getDisplayName() { - return PyBundle.message("INSP.NAME.method.over"); - } - - @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.inspNameMethodOver(); } + @Nonnull @Override - public void visitPyFunction(final PyFunction function) { - // sanity checks - PyClass cls = function.getContainingClass(); - if (cls == null) { - return; // not a method, ignore - } - String name = function.getName(); - if (PyNames.INIT.equals(name) || PyNames.NEW.equals(name)) { - return; // these are expected to change signature - } - // real work - for (PsiElement psiElement : PySuperMethodsSearch.search(function, myTypeEvalContext)) { - if (psiElement instanceof PyFunction) { - final PyFunction baseMethod = (PyFunction)psiElement; - final PyClass baseClass = baseMethod.getContainingClass(); - if (!PyUtil.isSignatureCompatibleTo(function, baseMethod, myTypeEvalContext)) { - final String msg = - PyBundle.message("INSP.signature.mismatch", cls.getName() + "." + name + "()", baseClass != null ? baseClass.getName() : ""); - registerProblem(function.getParameterList(), msg, new PyChangeSignatureQuickFix(true)); - } + 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 visitPyFunction(final PyFunction function) { + // sanity checks + PyClass cls = function.getContainingClass(); + if (cls == null) { + return; // not a method, ignore + } + String name = function.getName(); + if (PyNames.INIT.equals(name) || PyNames.NEW.equals(name)) { + return; // these are expected to change signature + } + // real work + for (PsiElement psiElement : PySuperMethodsSearch.search(function, myTypeEvalContext)) { + if (psiElement instanceof PyFunction) { + final PyFunction baseMethod = (PyFunction) psiElement; + final PyClass baseClass = baseMethod.getContainingClass(); + if (!PyUtil.isSignatureCompatibleTo(function, baseMethod, myTypeEvalContext)) { + LocalizeValue msg = PyLocalize.inspSignatureMismatch( + cls.getName() + "." + name + "()", + baseClass != null ? baseClass.getName() : "" + ); + registerProblem(function.getParameterList(), msg.get(), new PyChangeSignatureQuickFix(true)); + } + } + } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodParametersInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodParametersInspection.java index f35c660b..b6280bf0 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodParametersInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMethodParametersInspection.java @@ -16,7 +16,6 @@ package com.jetbrains.python.impl.inspections; import com.jetbrains.python.PyNames; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.inspections.quickfix.AddSelfQuickFix; import com.jetbrains.python.impl.inspections.quickfix.RenameParameterQuickFix; import com.jetbrains.python.impl.psi.PyUtil; @@ -33,12 +32,12 @@ import consulo.language.psi.PsiElement; import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.util.QualifiedName; +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import consulo.util.lang.ref.Ref; -import org.jetbrains.annotations.Nls; -import org.jetbrains.annotations.NonNls; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; +import org.jetbrains.annotations.NonNls; /** * Looks for the 'self' or its equivalents. @@ -46,233 +45,207 @@ * @author dcheryasov */ @ExtensionImpl -public class PyMethodParametersInspection extends PyInspection -{ - @Nonnull - @Override - public InspectionToolState createStateProvider() - { - return new PyMethodParametersInspectionState(); - } +public class PyMethodParametersInspection extends PyInspection { + @Nonnull + @Override + public InspectionToolState createStateProvider() { + return new PyMethodParametersInspectionState(); + } - @Nls - @Nonnull - public String getDisplayName() - { - return PyBundle.message("INSP.NAME.problematic.first.parameter"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameProblematicFirstParameter(); + } - @Nonnull - public HighlightDisplayLevel getDefaultLevel() - { - return HighlightDisplayLevel.WEAK_WARNING; - } + @Nonnull + public HighlightDisplayLevel getDefaultLevel() { + return HighlightDisplayLevel.WEAK_WARNING; + } - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) - { - return new Visitor(holder, session, (PyMethodParametersInspectionState) state); - } + @Nonnull + @Override + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session, (PyMethodParametersInspectionState) state); + } - public class Visitor extends PyInspectionVisitor - { - private Ref myPossibleZopeRef = null; - private final PyMethodParametersInspectionState myState; + public class Visitor extends PyInspectionVisitor { + private Ref myPossibleZopeRef = null; + private final PyMethodParametersInspectionState myState; - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session, PyMethodParametersInspectionState state) - { - super(holder, session); - myState = state; - } + public Visitor( + @Nullable ProblemsHolder holder, + @Nonnull LocalInspectionToolSession session, + PyMethodParametersInspectionState state + ) { + super(holder, session); + myState = state; + } - @Nullable - private PsiElement findZopeInterface(PsiElement foothold) - { - PsiElement ret; - synchronized(this) - { // other threads would wait as long in resolveInRoots() anyway - if(myPossibleZopeRef == null) - { - myPossibleZopeRef = new Ref<>(); - ret = ResolveImportUtil.resolveModuleInRoots(QualifiedName.fromComponents("zope.interface.Interface"), foothold); - myPossibleZopeRef.set(ret); // null is OK - } - else - { - ret = myPossibleZopeRef.get(); - } - } - return ret; - } + @Nullable + private PsiElement findZopeInterface(PsiElement foothold) { + PsiElement ret; + synchronized (this) { // other threads would wait as long in resolveInRoots() anyway + if (myPossibleZopeRef == null) { + myPossibleZopeRef = new Ref<>(); + ret = ResolveImportUtil.resolveModuleInRoots(QualifiedName.fromComponents("zope.interface.Interface"), foothold); + myPossibleZopeRef.set(ret); // null is OK + } + else { + ret = myPossibleZopeRef.get(); + } + } + return ret; + } - @Override - public void visitPyFunction(final PyFunction node) - { - for(PyInspectionExtension extension : PyInspectionExtension.EP_NAME.getExtensionList()) - { - if(extension.ignoreMethodParameters(node)) - { - return; - } - } - // maybe it's a zope interface? - PsiElement zope_interface = findZopeInterface(node); - final PyClass cls = node.getContainingClass(); - if(zope_interface instanceof PyClass) - { - if(cls != null && cls.isSubclass((PyClass) zope_interface, myTypeEvalContext)) - { - return; // it can have any params - } - } - // analyze function itself - PyUtil.MethodFlags flags = PyUtil.MethodFlags.of(node); - if(flags != null) - { - PyParameterList plist = node.getParameterList(); - PyParameter[] params = plist.getParameters(); - final String methodName = node.getName(); - final String CLS = "cls"; // TODO: move to style settings - if(params.length == 0) - { // fix: add - // check for "staticmetod" - if(flags.isStaticMethod()) - { - return; // no params may be fine - } - // check actual param list - ASTNode name_node = node.getNameNode(); - if(name_node != null) - { - PsiElement open_paren = plist.getFirstChild(); - PsiElement close_paren = plist.getLastChild(); - if(open_paren != null && close_paren != null && - "(".equals(open_paren.getText()) && ")".equals(close_paren.getText())) - { - String paramName; - if(flags.isMetaclassMethod()) - { - if(flags.isClassMethod()) - { - paramName = myState.MCS; - } - else - { - paramName = CLS; - } - } - else if(flags.isClassMethod()) - { - paramName = CLS; - } - else - { - paramName = PyNames.CANONICAL_SELF; - } - registerProblem(plist, - PyBundle.message("INSP.must.have.first.parameter", paramName), - ProblemHighlightType.GENERIC_ERROR, - null, - new AddSelfQuickFix(paramName)); - } - } - } - else - { // fix: rename - PyNamedParameter first_param = params[0].getAsNamed(); - if(first_param != null) - { - String pname = first_param.getName(); - if(pname == null) - { - return; - } - // every dup, swap, drop, or dup+drop of "self" - @NonNls String[] mangled = { - "eslf", - "sself", - "elf", - "felf", - "slef", - "seelf", - "slf", - "sslf", - "sefl", - "sellf", - "sef", - "seef" - }; - if(PyUtil.among(pname, mangled)) - { - registerProblem(PyUtil.sure(params[0].getNode()).getPsi(), - PyBundle.message("INSP.probably.mistyped.self"), - new RenameParameterQuickFix(PyNames.CANONICAL_SELF)); - return; - } - if(flags.isMetaclassMethod()) - { - if(flags.isStaticMethod() && !PyNames.NEW.equals(methodName)) - { - return; - } - String expectedName; - String alternativeName = null; - if(PyNames.NEW.equals(methodName) || flags.isClassMethod()) - { - expectedName = myState.MCS; - } - else if(flags.isSpecialMetaclassMethod()) - { - expectedName = CLS; - } - else - { - expectedName = PyNames.CANONICAL_SELF; - alternativeName = CLS; - } - if(!expectedName.equals(pname) && (alternativeName == null || !alternativeName.equals(pname))) - { - registerProblem(PyUtil.sure(params[0].getNode()).getPsi(), - PyBundle.message("INSP.usually.named.$0", expectedName), - new RenameParameterQuickFix(expectedName)); - } - } - else if(flags.isClassMethod() || - PyNames.NEW.equals(methodName) || - PyNames.INIT_SUBCLASS.equals(methodName) && LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON36)) - { - if(!CLS.equals(pname)) - { - registerProblem(PyUtil.sure(params[0].getNode()).getPsi(), - PyBundle.message("INSP.usually.named.$0", CLS), - new RenameParameterQuickFix(CLS)); - } - } - else if(!flags.isStaticMethod() && !first_param.isPositionalContainer() && !PyNames.CANONICAL_SELF.equals(pname)) - { - if(flags.isMetaclassMethod() && CLS.equals(pname)) - { - return; // accept either 'self' or 'cls' for all methods in metaclass - } - registerProblem(PyUtil.sure(params[0].getNode()).getPsi(), - PyBundle.message("INSP.usually.named.self"), - new RenameParameterQuickFix(PyNames.CANONICAL_SELF)); - } - } - else - { // the unusual case of a method with first tuple param - if(!flags.isStaticMethod()) - { - registerProblem(plist, PyBundle.message("INSP.first.param.must.not.be.tuple")); - } - } - } - } - } - } + @Override + public void visitPyFunction(final PyFunction node) { + for (PyInspectionExtension extension : PyInspectionExtension.EP_NAME.getExtensionList()) { + if (extension.ignoreMethodParameters(node)) { + return; + } + } + // maybe it's a zope interface? + PsiElement zope_interface = findZopeInterface(node); + final PyClass cls = node.getContainingClass(); + if (zope_interface instanceof PyClass) { + if (cls != null && cls.isSubclass((PyClass) zope_interface, myTypeEvalContext)) { + return; // it can have any params + } + } + // analyze function itself + PyUtil.MethodFlags flags = PyUtil.MethodFlags.of(node); + if (flags != null) { + PyParameterList plist = node.getParameterList(); + PyParameter[] params = plist.getParameters(); + final String methodName = node.getName(); + final String CLS = "cls"; // TODO: move to style settings + if (params.length == 0) { // fix: add + // check for "staticmetod" + if (flags.isStaticMethod()) { + return; // no params may be fine + } + // check actual param list + ASTNode name_node = node.getNameNode(); + if (name_node != null) { + PsiElement open_paren = plist.getFirstChild(); + PsiElement close_paren = plist.getLastChild(); + if (open_paren != null && close_paren != null && + "(".equals(open_paren.getText()) && ")".equals(close_paren.getText())) { + String paramName; + if (flags.isMetaclassMethod()) { + if (flags.isClassMethod()) { + paramName = myState.MCS; + } + else { + paramName = CLS; + } + } + else if (flags.isClassMethod()) { + paramName = CLS; + } + else { + paramName = PyNames.CANONICAL_SELF; + } + registerProblem( + plist, + PyLocalize.inspMustHaveFirstParameter(paramName).get(), + ProblemHighlightType.GENERIC_ERROR, + null, + new AddSelfQuickFix(paramName) + ); + } + } + } + else { // fix: rename + PyNamedParameter first_param = params[0].getAsNamed(); + if (first_param != null) { + String pname = first_param.getName(); + if (pname == null) { + return; + } + // every dup, swap, drop, or dup+drop of "self" + @NonNls String[] mangled = { + "eslf", + "sself", + "elf", + "felf", + "slef", + "seelf", + "slf", + "sslf", + "sefl", + "sellf", + "sef", + "seef" + }; + if (PyUtil.among(pname, mangled)) { + registerProblem( + PyUtil.sure(params[0].getNode()).getPsi(), + PyLocalize.inspProbablyMistypedSelf().get(), + new RenameParameterQuickFix(PyNames.CANONICAL_SELF) + ); + return; + } + if (flags.isMetaclassMethod()) { + if (flags.isStaticMethod() && !PyNames.NEW.equals(methodName)) { + return; + } + String expectedName; + String alternativeName = null; + if (PyNames.NEW.equals(methodName) || flags.isClassMethod()) { + expectedName = myState.MCS; + } + else if (flags.isSpecialMetaclassMethod()) { + expectedName = CLS; + } + else { + expectedName = PyNames.CANONICAL_SELF; + alternativeName = CLS; + } + if (!expectedName.equals(pname) && (alternativeName == null || !alternativeName.equals(pname))) { + registerProblem( + PyUtil.sure(params[0].getNode()).getPsi(), + PyLocalize.inspUsuallyNamed$0(expectedName).get(), + new RenameParameterQuickFix(expectedName) + ); + } + } + else if (flags.isClassMethod() || + PyNames.NEW.equals(methodName) || + PyNames.INIT_SUBCLASS.equals(methodName) && LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON36)) { + if (!CLS.equals(pname)) { + registerProblem( + PyUtil.sure(params[0].getNode()).getPsi(), + PyLocalize.inspUsuallyNamed$0(CLS).get(), + new RenameParameterQuickFix(CLS) + ); + } + } + else if (!flags.isStaticMethod() && !first_param.isPositionalContainer() && !PyNames.CANONICAL_SELF.equals(pname)) { + if (flags.isMetaclassMethod() && CLS.equals(pname)) { + return; // accept either 'self' or 'cls' for all methods in metaclass + } + registerProblem( + PyUtil.sure(params[0].getNode()).getPsi(), + PyLocalize.inspUsuallyNamedSelf().get(), + new RenameParameterQuickFix(PyNames.CANONICAL_SELF) + ); + } + } + else { // the unusual case of a method with first tuple param + if (!flags.isStaticMethod()) { + registerProblem(plist, PyLocalize.inspFirstParamMustNotBeTuple().get()); + } + } + } + } + } + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMissingConstructorInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMissingConstructorInspection.java index e3dbc6d6..46ccf5f9 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMissingConstructorInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMissingConstructorInspection.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.inspections.quickfix.AddCallSuperQuickFix; import com.jetbrains.python.impl.psi.PyUtil; import com.jetbrains.python.psi.*; @@ -26,192 +25,213 @@ import consulo.language.psi.PsiElement; import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiReference; +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import consulo.util.lang.Comparing; -import org.jetbrains.annotations.Nls; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; + import java.util.Optional; import static com.jetbrains.python.PyNames.*; /** - * User: catherine - *

* Inspection to warn if call to super constructor in class is missed + * + * @author catherine */ @ExtensionImpl public class PyMissingConstructorInspection extends PyInspection { - - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.missing.super.constructor"); - } - - @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 void visitPyClass(@Nonnull PyClass node) { - final PsiElement[] superClasses = node.getSuperClassExpressions(); - - if (superClasses.length == 0 || - superClasses.length == 1 && OBJECT.equals(superClasses[0].getText()) || - !superHasConstructor(node, myTypeEvalContext)) { - return; - } - - final PyFunction initMethod = node.findMethodByName(INIT, false, myTypeEvalContext); - - if (initMethod == null || isExceptionClass(node, myTypeEvalContext) || hasConstructorCall(node, initMethod, myTypeEvalContext)) { - return; - } - - if (superClasses.length == 1 || node.isNewStyleClass(myTypeEvalContext)) { - registerProblem(initMethod.getNameIdentifier(), - PyBundle.message("INSP.missing.super.constructor.message"), - new AddCallSuperQuickFix()); - } - else { - registerProblem(initMethod.getNameIdentifier(), PyBundle.message("INSP.missing.super.constructor.message")); - } - } - - private static boolean superHasConstructor(@Nonnull PyClass cls, @Nonnull TypeEvalContext context) { - final String className = cls.getName(); - - for (PyClass baseClass : cls.getAncestorClasses(context)) { - if (!PyUtil.isObjectClass(baseClass) && - !Comparing.equal(className, baseClass.getName()) && - baseClass.findMethodByName(INIT, false, context) != null) { - return true; - } - } - - return false; + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameMissingSuperConstructor(); } - private static boolean isExceptionClass(@Nonnull PyClass cls, @Nonnull TypeEvalContext context) { - if (PyBroadExceptionInspection.equalsException(cls, context)) { - return true; - } - - return cls.getAncestorClasses(context) - .stream() - .filter(baseClass -> PyBroadExceptionInspection.equalsException(baseClass, context)) - .findAny() - .isPresent(); - } - - private static boolean hasConstructorCall(@Nonnull PyClass cls, @Nonnull PyFunction initMethod, @Nonnull TypeEvalContext context) { - final CallVisitor visitor = new CallVisitor(cls, context); - initMethod.getStatementList().accept(visitor); - return visitor.myHasConstructorCall; + @Nonnull + @Override + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } - private static class CallVisitor extends PyRecursiveElementVisitor { - - @Nonnull - private final PyClass myClass; + private static class Visitor extends PyInspectionVisitor { - @Nonnull - private final TypeEvalContext myContext; - - private boolean myHasConstructorCall = false; - - public CallVisitor(@Nonnull PyClass cls, @Nonnull TypeEvalContext context) { - myClass = cls; - myContext = context; - } - - @Override - public void visitPyCallExpression(@Nonnull PyCallExpression node) { - if (isConstructorCall(node, myClass, myContext)) { - myHasConstructorCall = true; + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); } - } - - private static boolean isConstructorCall(@Nonnull PyCallExpression call, @Nonnull PyClass cls, @Nonnull TypeEvalContext context) { - final PyExpression callee = call.getCallee(); - if (callee == null || !INIT.equals(callee.getName())) { - return false; + @Override + public void visitPyClass(@Nonnull PyClass node) { + final PsiElement[] superClasses = node.getSuperClassExpressions(); + + if (superClasses.length == 0 || + superClasses.length == 1 && OBJECT.equals(superClasses[0].getText()) || + !superHasConstructor(node, myTypeEvalContext)) { + return; + } + + final PyFunction initMethod = node.findMethodByName(INIT, false, myTypeEvalContext); + + if (initMethod == null || isExceptionClass(node, myTypeEvalContext) || hasConstructorCall( + node, + initMethod, + myTypeEvalContext + )) { + return; + } + + if (superClasses.length == 1 || node.isNewStyleClass(myTypeEvalContext)) { + registerProblem( + initMethod.getNameIdentifier(), + PyLocalize.inspMissingSuperConstructorMessage().get(), + new AddCallSuperQuickFix() + ); + } + else { + registerProblem(initMethod.getNameIdentifier(), PyLocalize.inspMissingSuperConstructorMessage().get()); + } } - final PyExpression calleeQualifier = - Optional.of(callee).filter(PyQualifiedExpression.class::isInstance).map(PyQualifiedExpression.class::cast).map - (PyQualifiedExpression::getQualifier).orElse(null); + private static boolean superHasConstructor(@Nonnull PyClass cls, @Nonnull TypeEvalContext context) { + final String className = cls.getName(); - return calleeQualifier != null && (isSuperCall(calleeQualifier, cls, context) || isSuperClassCall(calleeQualifier, cls, context)); - } + for (PyClass baseClass : cls.getAncestorClasses(context)) { + if (!PyUtil.isObjectClass(baseClass) && + !Comparing.equal(className, baseClass.getName()) && + baseClass.findMethodByName(INIT, false, context) != null) { + return true; + } + } - private static boolean isSuperCall(@Nonnull PyExpression calleeQualifier, @Nonnull PyClass cls, @Nonnull TypeEvalContext context) { - final String prevCalleeName = Optional.of(calleeQualifier) - .filter(PyCallExpression.class::isInstance) - .map(PyCallExpression.class::cast) - .map(PyCallExpression::getCallee) - .map - (PyExpression::getName) - .orElse(null); - - if (!SUPER.equals(prevCalleeName)) { - return false; + return false; } - final PyExpression[] args = ((PyCallExpression)calleeQualifier).getArguments(); + private static boolean isExceptionClass(@Nonnull PyClass cls, @Nonnull TypeEvalContext context) { + if (PyBroadExceptionInspection.equalsException(cls, context)) { + return true; + } - if (args.length == 0) { - return true; + return cls.getAncestorClasses(context) + .stream() + .filter(baseClass -> PyBroadExceptionInspection.equalsException(baseClass, context)) + .findAny() + .isPresent(); } - final String firstArg = args[0].getText(); - final String classQName = cls.getQualifiedName(); - - if (firstArg.equals(cls.getName()) || - firstArg.equals(CANONICAL_SELF + "." + __CLASS__) || - classQName != null && classQName.endsWith(firstArg) || - firstArg.equals(__CLASS__) && LanguageLevel.forElement(cls).isAtLeast(LanguageLevel.PYTHON30)) { - return true; + private static boolean hasConstructorCall(@Nonnull PyClass cls, @Nonnull PyFunction initMethod, @Nonnull TypeEvalContext context) { + final CallVisitor visitor = new CallVisitor(cls, context); + initMethod.getStatementList().accept(visitor); + return visitor.myHasConstructorCall; } - return cls.getAncestorClasses(context).stream().map(PyClass::getName).filter(firstArg::equals).findAny().isPresent(); - } - - private static boolean isSuperClassCall(@Nonnull PyExpression calleeQualifier, - @Nonnull PyClass cls, - @Nonnull TypeEvalContext context) { - final PsiElement callingClass = resolveCallingClass(calleeQualifier); - - return callingClass != null && cls.getAncestorClasses(context).stream().filter(callingClass::equals).findAny().isPresent(); - } - - @Nullable - private static PsiElement resolveCallingClass(@Nonnull PyExpression calleeQualifier) { - if (calleeQualifier instanceof PyCallExpression) { - return Optional.of((PyCallExpression)calleeQualifier) - .map(PyCallExpression::getCallee) - .map(PyExpression::getReference) - .map(PsiReference::resolve) - .orElse(null); - } - else { - return Optional.ofNullable(calleeQualifier.getReference()).map(PsiReference::resolve).orElse(null); + private static class CallVisitor extends PyRecursiveElementVisitor { + + @Nonnull + private final PyClass myClass; + + @Nonnull + private final TypeEvalContext myContext; + + private boolean myHasConstructorCall = false; + + public CallVisitor(@Nonnull PyClass cls, @Nonnull TypeEvalContext context) { + myClass = cls; + myContext = context; + } + + @Override + public void visitPyCallExpression(@Nonnull PyCallExpression node) { + if (isConstructorCall(node, myClass, myContext)) { + myHasConstructorCall = true; + } + } + + private static boolean isConstructorCall( + @Nonnull PyCallExpression call, + @Nonnull PyClass cls, + @Nonnull TypeEvalContext context + ) { + final PyExpression callee = call.getCallee(); + + if (callee == null || !INIT.equals(callee.getName())) { + return false; + } + + final PyExpression calleeQualifier = + Optional.of(callee).filter(PyQualifiedExpression.class::isInstance).map(PyQualifiedExpression.class::cast).map + (PyQualifiedExpression::getQualifier).orElse(null); + + return calleeQualifier != null && (isSuperCall(calleeQualifier, cls, context) || isSuperClassCall( + calleeQualifier, + cls, + context + )); + } + + private static boolean isSuperCall( + @Nonnull PyExpression calleeQualifier, + @Nonnull PyClass cls, + @Nonnull TypeEvalContext context + ) { + final String prevCalleeName = Optional.of(calleeQualifier) + .filter(PyCallExpression.class::isInstance) + .map(PyCallExpression.class::cast) + .map(PyCallExpression::getCallee) + .map + (PyExpression::getName) + .orElse(null); + + if (!SUPER.equals(prevCalleeName)) { + return false; + } + + final PyExpression[] args = ((PyCallExpression) calleeQualifier).getArguments(); + + if (args.length == 0) { + return true; + } + + final String firstArg = args[0].getText(); + final String classQName = cls.getQualifiedName(); + + if (firstArg.equals(cls.getName()) || + firstArg.equals(CANONICAL_SELF + "." + __CLASS__) || + classQName != null && classQName.endsWith(firstArg) || + firstArg.equals(__CLASS__) && LanguageLevel.forElement(cls).isAtLeast(LanguageLevel.PYTHON30)) { + return true; + } + + return cls.getAncestorClasses(context).stream().map(PyClass::getName).filter(firstArg::equals).findAny().isPresent(); + } + + private static boolean isSuperClassCall( + @Nonnull PyExpression calleeQualifier, + @Nonnull PyClass cls, + @Nonnull TypeEvalContext context + ) { + final PsiElement callingClass = resolveCallingClass(calleeQualifier); + + return callingClass != null && cls.getAncestorClasses(context).stream().filter(callingClass::equals).findAny().isPresent(); + } + + @Nullable + private static PsiElement resolveCallingClass(@Nonnull PyExpression calleeQualifier) { + if (calleeQualifier instanceof PyCallExpression) { + return Optional.of((PyCallExpression) calleeQualifier) + .map(PyCallExpression::getCallee) + .map(PyExpression::getReference) + .map(PsiReference::resolve) + .orElse(null); + } + else { + return Optional.ofNullable(calleeQualifier.getReference()).map(PsiReference::resolve).orElse(null); + } + } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMissingOrEmptyDocstringInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMissingOrEmptyDocstringInspection.java index ff43f0fb..1ed04227 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMissingOrEmptyDocstringInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyMissingOrEmptyDocstringInspection.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.inspections.quickfix.DocstringQuickFix; import com.jetbrains.python.inspections.PyInspectionExtension; import com.jetbrains.python.psi.*; @@ -25,8 +24,9 @@ import consulo.language.editor.inspection.LocalInspectionToolSession; import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.psi.PsiElement; +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import consulo.util.lang.StringUtil; - import jakarta.annotation.Nonnull; /** @@ -34,63 +34,67 @@ */ @ExtensionImpl public class PyMissingOrEmptyDocstringInspection extends PyBaseDocstringInspection { - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.missing.or.empty.docstring"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameMissingOrEmptyDocstring(); + } - @Nonnull - @Override - public Visitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session) { - @Override - protected void checkDocString(@Nonnull PyDocStringOwner node) { - final PyStringLiteralExpression docStringExpression = node.getDocStringExpression(); - if (docStringExpression == null) { - for (PyInspectionExtension extension : PyInspectionExtension.EP_NAME.getExtensionList()) { - if (extension.ignoreMissingDocstring(node)) { - return; - } - } - PsiElement marker = null; - if (node instanceof PyClass) { - final ASTNode n = ((PyClass)node).getNameNode(); - if (n != null) { - marker = n.getPsi(); - } - } - else if (node instanceof PyFunction) { - final ASTNode n = ((PyFunction)node).getNameNode(); - if (n != null) { - marker = n.getPsi(); - } - } - else if (node instanceof PyFile) { - final TextRange tr = new TextRange(0, 0); - final ProblemsHolder holder = getHolder(); - if (holder != null) { - holder.registerProblem(node, tr, PyBundle.message("INSP.no.docstring")); + @Nonnull + @Override + public Visitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session) { + @Override + protected void checkDocString(@Nonnull PyDocStringOwner node) { + final PyStringLiteralExpression docStringExpression = node.getDocStringExpression(); + if (docStringExpression == null) { + for (PyInspectionExtension extension : PyInspectionExtension.EP_NAME.getExtensionList()) { + if (extension.ignoreMissingDocstring(node)) { + return; + } + } + PsiElement marker = null; + if (node instanceof PyClass) { + final ASTNode n = ((PyClass) node).getNameNode(); + if (n != null) { + marker = n.getPsi(); + } + } + else if (node instanceof PyFunction) { + final ASTNode n = ((PyFunction) node).getNameNode(); + if (n != null) { + marker = n.getPsi(); + } + } + else if (node instanceof PyFile) { + final TextRange tr = new TextRange(0, 0); + final ProblemsHolder holder = getHolder(); + if (holder != null) { + holder.newProblem(PyLocalize.inspNoDocstring()) + .range(node, tr) + .create(); + } + return; + } + if (marker == null) { + marker = node; + } + if (node instanceof PyFunction || (node instanceof PyClass && ((PyClass) node).findInitOrNew(false, null) != null)) { + registerProblem(marker, PyLocalize.inspNoDocstring().get(), new DocstringQuickFix(null, null)); + } + else { + registerProblem(marker, PyLocalize.inspNoDocstring().get()); + } + } + else if (StringUtil.isEmptyOrSpaces(docStringExpression.getStringValue())) { + registerProblem(docStringExpression, PyLocalize.inspEmptyDocstring().get()); + } } - return; - } - if (marker == null) { - marker = node; - } - if (node instanceof PyFunction || (node instanceof PyClass && ((PyClass)node).findInitOrNew(false, null) != null)) { - registerProblem(marker, PyBundle.message("INSP.no.docstring"), new DocstringQuickFix(null, null)); - } - else { - registerProblem(marker, PyBundle.message("INSP.no.docstring")); - } - } - else if (StringUtil.isEmptyOrSpaces(docStringExpression.getStringValue())) { - registerProblem(docStringExpression, PyBundle.message("INSP.empty.docstring")); - } - } - }; - } + }; + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyNestedDecoratorsInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyNestedDecoratorsInspection.java index 55708c0b..ca8cc8d2 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyNestedDecoratorsInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyNestedDecoratorsInspection.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.PyNames; import com.jetbrains.python.psi.PyDecorator; import com.jetbrains.python.psi.PyDecoratorList; @@ -27,63 +25,65 @@ import consulo.language.editor.inspection.ProblemsHolder; import consulo.language.editor.rawHighlight.HighlightDisplayLevel; 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; /** * Checks nested decorators, especially whatever comes after @classmethod. - *
- * User: dcheryasov - * Date: Sep 4, 2010 3:56:57 AM + * + * @author dcheryasov + * @since 2010-09-04 */ @ExtensionImpl public class PyNestedDecoratorsInspection extends PyInspection { - @Nls - @Nonnull - public String getDisplayName() { - return PyBundle.message("INSP.NAME.nested.decorators"); - } - - @Nonnull - public HighlightDisplayLevel getDefaultLevel() { - return HighlightDisplayLevel.WEAK_WARNING; - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameNestedDecorators(); + } - public static class Visitor extends PyInspectionVisitor { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); + @Nonnull + public HighlightDisplayLevel getDefaultLevel() { + return HighlightDisplayLevel.WEAK_WARNING; } + @Nonnull @Override - public void visitPyFunction(final PyFunction node) { - PyDecoratorList decolist = node.getDecoratorList(); - if (decolist != null) { - PyDecorator[] decos = decolist.getDecorators(); - if (decos.length > 1) { - for (int i = decos.length - 1; i >= 1; i -= 1) { - PyDecorator deco = decos[i]; - String deconame = deco.getName(); - if ((PyNames.CLASSMETHOD.equals(deconame) || PyNames.STATICMETHOD.equals(deconame)) && deco.isBuiltin()) { - registerProblem( - decos[i - 1], - PyBundle.message("INSP.decorator.receives.unexpected.builtin"), - ProblemHighlightType.GENERIC_ERROR_OR_WARNING - ); + 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 visitPyFunction(final PyFunction node) { + PyDecoratorList decolist = node.getDecoratorList(); + if (decolist != null) { + PyDecorator[] decos = decolist.getDecorators(); + if (decos.length > 1) { + for (int i = decos.length - 1; i >= 1; i -= 1) { + PyDecorator deco = decos[i]; + String deconame = deco.getName(); + if ((PyNames.CLASSMETHOD.equals(deconame) || PyNames.STATICMETHOD.equals(deconame)) && deco.isBuiltin()) { + registerProblem( + decos[i - 1], + PyLocalize.inspDecoratorReceivesUnexpectedBuiltin().get(), + ProblemHighlightType.GENERIC_ERROR_OR_WARNING + ); + } + } + } } - } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyNonAsciiCharInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyNonAsciiCharInspection.java index 426cd267..aba8c79e 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyNonAsciiCharInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyNonAsciiCharInspection.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.PythonFileType; import com.jetbrains.python.impl.inspections.quickfix.AddEncodingQuickFix; import com.jetbrains.python.psi.LanguageLevel; @@ -29,100 +27,90 @@ import consulo.language.psi.PsiElement; import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiFile; - +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; + import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; /** - * User : catherine + * @author catherine */ @ExtensionImpl -public class PyNonAsciiCharInspection extends PyInspection -{ - @Nonnull - @Override - public InspectionToolState createStateProvider() - { - return new PyNonAsciiCharInspectionState(); - } +public class PyNonAsciiCharInspection extends PyInspection { + @Nonnull + @Override + public InspectionToolState createStateProvider() { + return new PyNonAsciiCharInspectionState(); + } - @Nonnull - @Override - public String getDisplayName() - { - return PyBundle.message("INSP.NAME.non.ascii"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameNonAscii(); + } - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) - { - return new Visitor(holder, session, (PyNonAsciiCharInspectionState) state); - } + @Nonnull + @Override + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session, (PyNonAsciiCharInspectionState) state); + } - private class Visitor extends PyInspectionVisitor - { - private final PyNonAsciiCharInspectionState myState; + private class Visitor extends PyInspectionVisitor { + private final PyNonAsciiCharInspectionState myState; - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session, PyNonAsciiCharInspectionState state) - { - super(holder, session); - myState = state; - } + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session, PyNonAsciiCharInspectionState state) { + super(holder, session); + myState = state; + } - @Override - public void visitComment(PsiComment node) - { - checkString(node, node.getText()); - } + @Override + public void visitComment(PsiComment node) { + checkString(node, node.getText()); + } - private void checkString(PsiElement node, String value) - { - if(LanguageLevel.forElement(node).isPy3K()) - { - return; - } - PsiFile file = node.getContainingFile(); // can't cache this in the instance, alas - if(file == null) - { - return; - } - final String charsetString = PythonFileType.getCharsetFromEncodingDeclaration(file.getText()); + private void checkString(PsiElement node, String value) { + if (LanguageLevel.forElement(node).isPy3K()) { + return; + } + PsiFile file = node.getContainingFile(); // can't cache this in the instance, alas + if (file == null) { + return; + } + final String charsetString = PythonFileType.getCharsetFromEncodingDeclaration(file.getText()); - boolean hasNonAscii = false; + boolean hasNonAscii = false; - CharsetEncoder asciiEncoder = Charset.forName("US-ASCII").newEncoder(); - int length = value.length(); - char c = 0; - for(int i = 0; i < length; ++i) - { - c = value.charAt(i); - if(!asciiEncoder.canEncode(c)) - { - hasNonAscii = true; - break; - } - } + CharsetEncoder asciiEncoder = Charset.forName("US-ASCII").newEncoder(); + int length = value.length(); + char c = 0; + for (int i = 0; i < length; ++i) { + c = value.charAt(i); + if (!asciiEncoder.canEncode(c)) { + hasNonAscii = true; + break; + } + } - if(hasNonAscii) - { - if(charsetString == null) - { - registerProblem(node, "Non-ASCII character " + c + " in file, but no encoding declared", - new AddEncodingQuickFix(myState.myDefaultEncoding, myState.myEncodingFormatIndex)); - } - } - } + if (hasNonAscii) { + if (charsetString == null) { + registerProblem(node, "Non-ASCII character " + c + " in file, but no encoding declared", + new AddEncodingQuickFix(myState.myDefaultEncoding, myState.myEncodingFormatIndex) + ); + } + } + } - @Override - public void visitPyStringLiteralExpression(PyStringLiteralExpression node) - { - checkString(node, node.getText()); - } - } + @Override + public void visitPyStringLiteralExpression(PyStringLiteralExpression node) { + checkString(node, node.getText()); + } + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyNoneFunctionAssignmentInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyNoneFunctionAssignmentInspection.java index 19cd0383..2b14f19f 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyNoneFunctionAssignmentInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyNoneFunctionAssignmentInspection.java @@ -15,93 +15,98 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.inspections.quickfix.PyRemoveAssignmentQuickFix; import com.jetbrains.python.impl.psi.PyUtil; -import com.jetbrains.python.psi.*; import com.jetbrains.python.impl.psi.search.PyOverridingMethodsSearch; import com.jetbrains.python.impl.psi.types.PyNoneType; -import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.impl.sdk.PySdkUtil; +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; + import java.util.HashMap; import java.util.Map; /** - * User: ktisha - *

- * pylint E1111 - *

- * Used when an assignment is done on a function call but the inferred function doesn't return anything. + *

pylint E1111

+ * + *

Used when an assignment is done on a function call but the inferred function doesn't return anything.

+ * + * @author ktisha */ @ExtensionImpl public class PyNoneFunctionAssignmentInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.none.function.assignment"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameNoneFunctionAssignment(); + } - @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 final Map myHasInheritors = new HashMap<>(); + private static class Visitor extends PyInspectionVisitor { + private final Map myHasInheritors = new HashMap<>(); - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); - } + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } - @Override - public void visitPyAssignmentStatement(PyAssignmentStatement node) { - final PyExpression value = node.getAssignedValue(); - if (value instanceof PyCallExpression) { - final PyType type = myTypeEvalContext.getType(value); - final PyCallExpression callExpr = (PyCallExpression)value; - final PyExpression callee = callExpr.getCallee(); + @Override + public void visitPyAssignmentStatement(PyAssignmentStatement node) { + final PyExpression value = node.getAssignedValue(); + if (value instanceof PyCallExpression) { + final PyType type = myTypeEvalContext.getType(value); + final PyCallExpression callExpr = (PyCallExpression) value; + final PyExpression callee = callExpr.getCallee(); - if (type instanceof PyNoneType && callee != null) { - final PyCallable callable = callExpr.resolveCalleeFunction(getResolveContext()); - if (callable != null) { - if (PySdkUtil.isElementInSkeletons(callable)) { - return; + if (type instanceof PyNoneType && callee != null) { + final PyCallable callable = callExpr.resolveCalleeFunction(getResolveContext()); + if (callable != null) { + if (PySdkUtil.isElementInSkeletons(callable)) { + return; + } + if (callable instanceof PyFunction) { + final PyFunction function = (PyFunction) callable; + // Currently we don't infer types returned by decorators + if (hasInheritors(function) || PyUtil.hasCustomDecorators(function)) { + return; + } + } + registerProblem( + node, + PyLocalize.inspNoneFunctionAssignment(callee.getName()).get(), + new PyRemoveAssignmentQuickFix() + ); + } + } } - if (callable instanceof PyFunction) { - final PyFunction function = (PyFunction)callable; - // Currently we don't infer types returned by decorators - if (hasInheritors(function) || PyUtil.hasCustomDecorators(function)) { - return; - } - } - registerProblem(node, PyBundle.message("INSP.none.function.assignment", callee.getName()), new PyRemoveAssignmentQuickFix()); - } } - } - } - private boolean hasInheritors(@Nonnull PyFunction function) { - final Boolean cached = myHasInheritors.get(function); - if (cached != null) { - return cached; - } - final boolean result = PyOverridingMethodsSearch.search(function, true).findFirst() != null; - myHasInheritors.put(function, result); - return result; + private boolean hasInheritors(@Nonnull PyFunction function) { + final Boolean cached = myHasInheritors.get(function); + if (cached != null) { + return cached; + } + final boolean result = PyOverridingMethodsSearch.search(function, true).findFirst() != null; + myHasInheritors.put(function, result); + return result; + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyOldStyleClassesInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyOldStyleClassesInspection.java index 545a9bec..49fa1a76 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyOldStyleClassesInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyOldStyleClassesInspection.java @@ -16,7 +16,6 @@ package com.jetbrains.python.impl.inspections; import com.google.common.collect.Lists; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.impl.inspections.quickfix.PyChangeBaseClassQuickFix; import com.jetbrains.python.impl.inspections.quickfix.PyConvertToNewStyleQuickFix; @@ -32,107 +31,114 @@ 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; + import java.util.List; /** - * User: catherine - *

* Inspection to detect occurrences of new-style class features in old-style classes + * + * @author catherine */ @ExtensionImpl public class PyOldStyleClassesInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.oldstyle.class"); - } - - @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.inspNameOldstyleClass(); } + @Nonnull @Override - public void visitPyClass(final PyClass node) { - final List expressions = node.getSuperClassTypes(myTypeEvalContext); - List quickFixes = Lists.newArrayList(new PyConvertToNewStyleQuickFix()); - if (!expressions.isEmpty()) { - quickFixes.add(new PyChangeBaseClassQuickFix()); - } - if (!node.isNewStyleClass(myTypeEvalContext)) { - for (PyTargetExpression attr : node.getClassAttributes()) { - if (PyNames.SLOTS.equals(attr.getName())) { - registerProblem(attr, - PyBundle.message("INSP.oldstyle.class.slots"), + 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 visitPyClass(final PyClass node) { + final List expressions = node.getSuperClassTypes(myTypeEvalContext); + List quickFixes = Lists.newArrayList(new PyConvertToNewStyleQuickFix()); + if (!expressions.isEmpty()) { + quickFixes.add(new PyChangeBaseClassQuickFix()); + } + if (!node.isNewStyleClass(myTypeEvalContext)) { + for (PyTargetExpression attr : node.getClassAttributes()) { + if (PyNames.SLOTS.equals(attr.getName())) { + registerProblem( + attr, + PyLocalize.inspOldstyleClassSlots().get(), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, null, - quickFixes.toArray(new LocalQuickFix[quickFixes.size - ()])); - } - } - for (PyFunction attr : node.getMethods()) { - if (PyNames.GETATTRIBUTE.equals(attr.getName())) { - final ASTNode nameNode = attr.getNameNode(); - assert nameNode != null; - registerProblem(nameNode.getPsi(), - PyBundle.message("INSP.oldstyle.class.getattribute"), + quickFixes.toArray(new LocalQuickFix[quickFixes.size()]) + ); + } + } + for (PyFunction attr : node.getMethods()) { + if (PyNames.GETATTRIBUTE.equals(attr.getName())) { + final ASTNode nameNode = attr.getNameNode(); + assert nameNode != null; + registerProblem( + nameNode.getPsi(), + PyLocalize.inspOldstyleClassGetattribute().get(), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, null, quickFixes.toArray(new - LocalQuickFix[quickFixes.size()])); - } + LocalQuickFix[quickFixes.size()]) + ); + } + } + } } - } - } - @Override - public void visitPyCallExpression(final PyCallExpression node) { - PyClass klass = PsiTreeUtil.getParentOfType(node, PyClass.class); - if (klass != null && !klass.isNewStyleClass(myTypeEvalContext)) { - final List types = klass.getSuperClassTypes(myTypeEvalContext); - for (PyClassLikeType type : types) { - if (type == null) { - return; - } - final String qName = type.getClassQName(); - if (qName != null && qName.contains("PyQt")) { - return; - } - if (!(type instanceof PyClassType)) { - return; - } - } - List quickFixes = Lists.newArrayList(new PyConvertToNewStyleQuickFix()); - if (!types.isEmpty()) { - quickFixes.add(new PyChangeBaseClassQuickFix()); - } + @Override + public void visitPyCallExpression(final PyCallExpression node) { + PyClass klass = PsiTreeUtil.getParentOfType(node, PyClass.class); + if (klass != null && !klass.isNewStyleClass(myTypeEvalContext)) { + final List types = klass.getSuperClassTypes(myTypeEvalContext); + for (PyClassLikeType type : types) { + if (type == null) { + return; + } + final String qName = type.getClassQName(); + if (qName != null && qName.contains("PyQt")) { + return; + } + if (!(type instanceof PyClassType)) { + return; + } + } + List quickFixes = Lists.newArrayList(new PyConvertToNewStyleQuickFix()); + if (!types.isEmpty()) { + quickFixes.add(new PyChangeBaseClassQuickFix()); + } - if (PyUtil.isSuperCall(node)) { - final PyExpression callee = node.getCallee(); - if (callee != null) { - registerProblem(callee, - PyBundle.message("INSP.oldstyle.class.super"), + if (PyUtil.isSuperCall(node)) { + final PyExpression callee = node.getCallee(); + if (callee != null) { + registerProblem( + callee, + PyLocalize.inspOldstyleClassSuper().get(), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, null, quickFixes.toArray(quickFixes.toArray(new - LocalQuickFix[quickFixes.size()]))); - } + LocalQuickFix[quickFixes.size()])) + ); + } + } + } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyPep8Inspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyPep8Inspection.java index b7caf16e..4fe268ef 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyPep8Inspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyPep8Inspection.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.inspections; import consulo.annotation.component.ExtensionImpl; import consulo.language.editor.inspection.InspectionToolState; +import consulo.localize.LocalizeValue; import consulo.util.dataholder.Key; import jakarta.annotation.Nonnull; @@ -29,22 +29,19 @@ * @author yole */ @ExtensionImpl -public class PyPep8Inspection extends PyInspection -{ - public static final String INSPECTION_SHORT_NAME = "PyPep8Inspection"; - public static final Key KEY = Key.create(INSPECTION_SHORT_NAME); +public class PyPep8Inspection extends PyInspection { + public static final String INSPECTION_SHORT_NAME = "PyPep8Inspection"; + public static final Key KEY = Key.create(INSPECTION_SHORT_NAME); - @Nonnull - @Override - public InspectionToolState createStateProvider() - { - return new PyPep8InspectionState(); - } + @Nonnull + @Override + public InspectionToolState createStateProvider() { + return new PyPep8InspectionState(); + } - @Nonnull - @Override - public String getDisplayName() - { - return "PEP 8 coding style violation"; - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return LocalizeValue.localizeTODO("PEP 8 coding style violation"); + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyPropertyAccessInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyPropertyAccessInspection.java index 4ee4639a..f13836d0 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyPropertyAccessInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyPropertyAccessInspection.java @@ -15,7 +15,6 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.inspections.quickfix.PyCreatePropertyQuickFix; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.types.PyClassType; @@ -26,106 +25,109 @@ 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.Pair; -import org.jetbrains.annotations.Nls; - import jakarta.annotation.Nonnull; + import java.util.HashMap; /** * Checks that properties are accessed correctly. - * User: dcheryasov - * Date: Jun 29, 2010 5:55:52 AM + * + * @author dcheryasov + * @since 2010-06-29 */ @ExtensionImpl public class PyPropertyAccessInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.property.access"); - } - - @Override - public boolean isEnabledByDefault() { - return true; - } - - @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 { - private final HashMap, Property> myPropertyCache = new HashMap<>(); - - public Visitor(@Nonnull final ProblemsHolder holder, LocalInspectionToolSession session) { - super(holder, session); + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNamePropertyAccess(); } @Override - public void visitPyReferenceExpression(PyReferenceExpression node) { - super.visitPyReferenceExpression(node); - checkPropertyExpression(node); + public boolean isEnabledByDefault() { + return true; } + @Nonnull @Override - public void visitPyTargetExpression(PyTargetExpression node) { - super.visitPyTargetExpression(node); - checkPropertyExpression(node); + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } - private void checkPropertyExpression(PyQualifiedExpression node) { - final PyExpression qualifier = node.getQualifier(); - if (qualifier != null) { - final PyType type = myTypeEvalContext.getType(qualifier); - if (type instanceof PyClassType) { - final PyClass cls = ((PyClassType)type).getPyClass(); - final String name = node.getName(); - if (name != null) { - final Pair key = Pair.create(cls, name); - final Property property; - if (myPropertyCache.containsKey(key)) { - property = myPropertyCache.get(key); - } - else { - property = cls.findProperty(name, true, myTypeEvalContext); - } - myPropertyCache.put(key, property); // we store nulls, too, to know that a property does not exist - if (property != null) { - final AccessDirection dir = AccessDirection.of(node); - checkAccessor(node, name, dir, property); - if (dir == AccessDirection.READ) { - final PsiElement parent = node.getParent(); - if (parent instanceof PyAugAssignmentStatement && ((PyAugAssignmentStatement)parent).getTarget() == node) { - checkAccessor(node, name, AccessDirection.WRITE, property); - } - } - } - } + public static class Visitor extends PyInspectionVisitor { + private final HashMap, Property> myPropertyCache = new HashMap<>(); + + public Visitor(@Nonnull final ProblemsHolder holder, LocalInspectionToolSession session) { + super(holder, session); + } + + @Override + public void visitPyReferenceExpression(PyReferenceExpression node) { + super.visitPyReferenceExpression(node); + checkPropertyExpression(node); } - } - } - private void checkAccessor(PyExpression node, String name, AccessDirection dir, Property property) { - final Maybe accessor = property.getByDirection(dir); - if (accessor.isDefined() && accessor.value() == null) { - final String message; - if (dir == AccessDirection.WRITE) { - message = PyBundle.message("INSP.property.$0.cant.be.set", name); + @Override + public void visitPyTargetExpression(PyTargetExpression node) { + super.visitPyTargetExpression(node); + checkPropertyExpression(node); } - else if (dir == AccessDirection.DELETE) { - message = PyBundle.message("INSP.property.$0.cant.be.deleted", name); + + private void checkPropertyExpression(PyQualifiedExpression node) { + final PyExpression qualifier = node.getQualifier(); + if (qualifier != null) { + final PyType type = myTypeEvalContext.getType(qualifier); + if (type instanceof PyClassType) { + final PyClass cls = ((PyClassType) type).getPyClass(); + final String name = node.getName(); + if (name != null) { + final Pair key = Pair.create(cls, name); + final Property property; + if (myPropertyCache.containsKey(key)) { + property = myPropertyCache.get(key); + } + else { + property = cls.findProperty(name, true, myTypeEvalContext); + } + myPropertyCache.put(key, property); // we store nulls, too, to know that a property does not exist + if (property != null) { + final AccessDirection dir = AccessDirection.of(node); + checkAccessor(node, name, dir, property); + if (dir == AccessDirection.READ) { + final PsiElement parent = node.getParent(); + if (parent instanceof PyAugAssignmentStatement && ((PyAugAssignmentStatement) parent).getTarget() == node) { + checkAccessor(node, name, AccessDirection.WRITE, property); + } + } + } + } + } + } } - else { - message = PyBundle.message("INSP.property.$0.cant.be.read", name); + + private void checkAccessor(PyExpression node, String name, AccessDirection dir, Property property) { + final Maybe accessor = property.getByDirection(dir); + if (accessor.isDefined() && accessor.value() == null) { + LocalizeValue message; + if (dir == AccessDirection.WRITE) { + message = PyLocalize.inspProperty$0CantBeSet(name); + } + else if (dir == AccessDirection.DELETE) { + message = PyLocalize.inspProperty$0CantBeDeleted(name); + } + else { + message = PyLocalize.inspProperty$0CantBeRead(name); + } + registerProblem(node, message.get(), new PyCreatePropertyQuickFix(dir)); + } } - registerProblem(node, message, new PyCreatePropertyQuickFix(dir)); - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyPropertyDefinitionInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyPropertyDefinitionInspection.java index 5cd24bdd..b77255e5 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyPropertyDefinitionInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyPropertyDefinitionInspection.java @@ -17,7 +17,6 @@ import com.google.common.collect.ImmutableList; import com.jetbrains.python.PyNames; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.impl.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.impl.inspections.quickfix.PyUpdatePropertySignatureQuickFix; import com.jetbrains.python.impl.inspections.quickfix.RenameParameterQuickFix; @@ -40,12 +39,13 @@ import consulo.language.psi.PsiPolyVariantReference; import consulo.language.psi.util.PsiTreeUtil; import consulo.language.psi.util.QualifiedName; +import consulo.localize.LocalizeValue; +import consulo.python.impl.localize.PyLocalize; import consulo.util.lang.Comparing; import consulo.util.lang.ref.Ref; -import org.jetbrains.annotations.Nls; - import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -53,317 +53,337 @@ /** * Checks that arguments to property() and @property and friends are ok. - *
- * User: dcheryasov - * Date: Jun 30, 2010 2:53:05 PM + * + * @author dcheryasov + * @since 2010-06-30 */ @ExtensionImpl public class PyPropertyDefinitionInspection extends PyInspection { + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNamePropertyDefinition(); + } - @Nls - @Nonnull - public String getDisplayName() { - return PyBundle.message("INSP.NAME.property.definition"); - } - - private static final ImmutableList SUFFIXES = ImmutableList.of(PyNames.SETTER, PyNames.DELETER); + private static final ImmutableList SUFFIXES = ImmutableList.of(PyNames.SETTER, PyNames.DELETER); - @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); + } - public static class Visitor extends PyInspectionVisitor { + public static class Visitor extends PyInspectionVisitor { - private LanguageLevel myLevel; - private List myStringClasses; - private PyFunction myOneParamFunction; - private PyFunction myTwoParamFunction; // arglist with two args, 'self' and 'value' + private LanguageLevel myLevel; + private List myStringClasses; + private PyFunction myOneParamFunction; + private PyFunction myTwoParamFunction; // arglist with two args, 'self' and 'value' - public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { - super(holder, session); - PsiFile psiFile = session.getFile(); - // save us continuous checks for level, module, stc - myLevel = LanguageLevel.forElement(psiFile); - // string classes - final List stringClasses = new ArrayList<>(2); - final PyBuiltinCache builtins = PyBuiltinCache.getInstance(psiFile); - PyClass cls = builtins.getClass("str"); - if (cls != null) { - stringClasses.add(cls); - } - cls = builtins.getClass("unicode"); - if (cls != null) { - stringClasses.add(cls); - } - myStringClasses = stringClasses; - // reference signatures - PyClass objectClass = builtins.getClass("object"); - if (objectClass != null) { - final PyFunction methodRepr = objectClass.findMethodByName("__repr__", false, null); - if (methodRepr != null) { - myOneParamFunction = methodRepr; - } - final PyFunction methodDelattr = objectClass.findMethodByName("__delattr__", false, null); - if (methodDelattr != null) { - myTwoParamFunction = methodDelattr; + public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) { + super(holder, session); + PsiFile psiFile = session.getFile(); + // save us continuous checks for level, module, stc + myLevel = LanguageLevel.forElement(psiFile); + // string classes + final List stringClasses = new ArrayList<>(2); + final PyBuiltinCache builtins = PyBuiltinCache.getInstance(psiFile); + PyClass cls = builtins.getClass("str"); + if (cls != null) { + stringClasses.add(cls); + } + cls = builtins.getClass("unicode"); + if (cls != null) { + stringClasses.add(cls); + } + myStringClasses = stringClasses; + // reference signatures + PyClass objectClass = builtins.getClass("object"); + if (objectClass != null) { + final PyFunction methodRepr = objectClass.findMethodByName("__repr__", false, null); + if (methodRepr != null) { + myOneParamFunction = methodRepr; + } + final PyFunction methodDelattr = objectClass.findMethodByName("__delattr__", false, null); + if (methodDelattr != null) { + myTwoParamFunction = methodDelattr; + } + } } - } - } - - @Override - public void visitPyFile(PyFile node) { - super.visitPyFile(node); - } - @Override - public void visitPyClass(final PyClass node) { - super.visitPyClass(node); - // check property() and @property - node.scanProperties(property -> { - PyTargetExpression target = property.getDefinitionSite(); - if (target != null) { - // target = property(); args may be all funny - PyCallExpression call = (PyCallExpression)target.findAssignedValue(); - assert call != null : "Property has a null call assigned to it"; - final PyArgumentList arglist = call.getArgumentList(); - assert arglist != null : "Property call has null arglist"; - // we assume fget, fset, fdel, doc names - final PyCallExpression.PyArgumentsMapping mapping = call.mapArguments(getResolveContext()); - for (Map.Entry entry : mapping.getMappedParameters().entrySet()) { - final String paramName = entry.getValue().getName(); - PyExpression argument = PyUtil.peelArgument(entry.getKey()); - checkPropertyCallArgument(paramName, argument, node.getContainingFile()); - } + @Override + public void visitPyFile(PyFile node) { + super.visitPyFile(node); } - else { - // @property; we only check getter, others are checked by visitPyFunction - // getter is always present with this form - final PyCallable callable = property.getGetter().valueOrNull(); - if (callable instanceof PyFunction) { - checkGetter(callable, getFunctionMarkingElement((PyFunction)callable)); - } - } - return false; // always want more - }, false); - } - private void checkPropertyCallArgument(String paramName, PyExpression argument, PsiFile containingFile) { - assert argument != null : "Parameter mapped to null argument"; - PyCallable callable = null; - if (argument instanceof PyReferenceExpression) { - final PsiPolyVariantReference reference = ((PyReferenceExpression)argument).getReference(getResolveContext()); - PsiElement resolved = reference.resolve(); - if (resolved instanceof PyCallable) { - callable = (PyCallable)resolved; - } - else { - reportNonCallableArg(resolved, argument); - return; + @Override + public void visitPyClass(final PyClass node) { + super.visitPyClass(node); + // check property() and @property + node.scanProperties(property -> { + PyTargetExpression target = property.getDefinitionSite(); + if (target != null) { + // target = property(); args may be all funny + PyCallExpression call = (PyCallExpression) target.findAssignedValue(); + assert call != null : "Property has a null call assigned to it"; + final PyArgumentList arglist = call.getArgumentList(); + assert arglist != null : "Property call has null arglist"; + // we assume fget, fset, fdel, doc names + final PyCallExpression.PyArgumentsMapping mapping = call.mapArguments(getResolveContext()); + for (Map.Entry entry : mapping.getMappedParameters().entrySet()) { + final String paramName = entry.getValue().getName(); + PyExpression argument = PyUtil.peelArgument(entry.getKey()); + checkPropertyCallArgument(paramName, argument, node.getContainingFile()); + } + } + else { + // @property; we only check getter, others are checked by visitPyFunction + // getter is always present with this form + final PyCallable callable = property.getGetter().valueOrNull(); + if (callable instanceof PyFunction) { + checkGetter(callable, getFunctionMarkingElement((PyFunction) callable)); + } + } + return false; // always want more + }, false); } - } - else if (argument instanceof PyLambdaExpression) { - callable = (PyLambdaExpression)argument; - } - else if (!"doc".equals(paramName)) { - reportNonCallableArg(argument, argument); - return; - } - if (callable != null && callable.getContainingFile() != containingFile) { - return; - } - if ("fget".equals(paramName)) { - checkGetter(callable, argument); - } - else if ("fset".equals(paramName)) { - checkSetter(callable, argument); - } - else if ("fdel".equals(paramName)) { - checkDeleter(callable, argument); - } - else if ("doc".equals(paramName)) { - PyType type = myTypeEvalContext.getType(argument); - if (!(type instanceof PyClassType && myStringClasses.contains(((PyClassType)type).getPyClass()))) { - registerProblem(argument, PyBundle.message("INSP.doc.param.should.be.str")); + + private void checkPropertyCallArgument(String paramName, PyExpression argument, PsiFile containingFile) { + assert argument != null : "Parameter mapped to null argument"; + PyCallable callable = null; + if (argument instanceof PyReferenceExpression) { + final PsiPolyVariantReference reference = ((PyReferenceExpression) argument).getReference(getResolveContext()); + PsiElement resolved = reference.resolve(); + if (resolved instanceof PyCallable) { + callable = (PyCallable) resolved; + } + else { + reportNonCallableArg(resolved, argument); + return; + } + } + else if (argument instanceof PyLambdaExpression) { + callable = (PyLambdaExpression) argument; + } + else if (!"doc".equals(paramName)) { + reportNonCallableArg(argument, argument); + return; + } + if (callable != null && callable.getContainingFile() != containingFile) { + return; + } + if ("fget".equals(paramName)) { + checkGetter(callable, argument); + } + else if ("fset".equals(paramName)) { + checkSetter(callable, argument); + } + else if ("fdel".equals(paramName)) { + checkDeleter(callable, argument); + } + else if ("doc".equals(paramName)) { + PyType type = myTypeEvalContext.getType(argument); + if (!(type instanceof PyClassType && myStringClasses.contains(((PyClassType) type).getPyClass()))) { + registerProblem(argument, PyLocalize.inspDocParamShouldBeStr().get()); + } + } } - } - } - private void reportNonCallableArg(PsiElement resolved, PsiElement element) { - if (resolved instanceof PySubscriptionExpression || resolved instanceof PyNoneLiteralExpression) { - return; - } - if (PyNames.NONE.equals(element.getText())) { - return; - } - if (resolved instanceof PyTypedElement) { - final PyType type = myTypeEvalContext.getType((PyTypedElement)resolved); - final Boolean isCallable = PyTypeChecker.isCallable(type); - if (isCallable != null && !isCallable) { - registerProblem(element, PyBundle.message("INSP.strange.arg.want.callable")); + private void reportNonCallableArg(PsiElement resolved, PsiElement element) { + if (resolved instanceof PySubscriptionExpression || resolved instanceof PyNoneLiteralExpression) { + return; + } + if (PyNames.NONE.equals(element.getText())) { + return; + } + if (resolved instanceof PyTypedElement) { + final PyType type = myTypeEvalContext.getType((PyTypedElement) resolved); + final Boolean isCallable = PyTypeChecker.isCallable(type); + if (isCallable != null && !isCallable) { + registerProblem(element, PyLocalize.inspStrangeArgWantCallable().get()); + } + } } - } - } - @Override - public void visitPyFunction(PyFunction node) { - super.visitPyFunction(node); - if (myLevel.isAtLeast(LanguageLevel.PYTHON26)) { - // check @foo.setter and @foo.deleter - PyClass cls = node.getContainingClass(); - if (cls != null) { - final PyDecoratorList decos = node.getDecoratorList(); - if (decos != null) { - String name = node.getName(); - for (PyDecorator deco : decos.getDecorators()) { - final QualifiedName qName = deco.getQualifiedName(); - if (qName != null) { - List nameParts = qName.getComponents(); - if (nameParts.size() == 2) { - final int suffixIndex = SUFFIXES.indexOf(nameParts.get(1)); - if (suffixIndex >= 0) { - if (Comparing.equal(name, nameParts.get(0))) { - // names are ok, what about signatures? - PsiElement markable = getFunctionMarkingElement(node); - if (suffixIndex == 0) { - checkSetter(node, markable); - } - else { - checkDeleter(node, markable); - } + @Override + public void visitPyFunction(PyFunction node) { + super.visitPyFunction(node); + if (myLevel.isAtLeast(LanguageLevel.PYTHON26)) { + // check @foo.setter and @foo.deleter + PyClass cls = node.getContainingClass(); + if (cls != null) { + final PyDecoratorList decos = node.getDecoratorList(); + if (decos != null) { + String name = node.getName(); + for (PyDecorator deco : decos.getDecorators()) { + final QualifiedName qName = deco.getQualifiedName(); + if (qName != null) { + List nameParts = qName.getComponents(); + if (nameParts.size() == 2) { + final int suffixIndex = SUFFIXES.indexOf(nameParts.get(1)); + if (suffixIndex >= 0) { + if (Comparing.equal(name, nameParts.get(0))) { + // names are ok, what about signatures? + PsiElement markable = getFunctionMarkingElement(node); + if (suffixIndex == 0) { + checkSetter(node, markable); + } + else { + checkDeleter(node, markable); + } + } + else { + registerProblem(deco, PyLocalize.inspFuncPropertyNameMismatch().get()); + } + } + } + } + } } - else { - registerProblem(deco, PyBundle.message("INSP.func.property.name.mismatch")); - } - } } - } } - } } - } - } - - @Nullable - private static PsiElement getFunctionMarkingElement(PyFunction node) { - if (node == null) { - return null; - } - final ASTNode nameNode = node.getNameNode(); - PsiElement markable = node; - if (nameNode != null) { - markable = nameNode.getPsi(); - } - return markable; - } + @Nullable + private static PsiElement getFunctionMarkingElement(PyFunction node) { + if (node == null) { + return null; + } + final ASTNode nameNode = node.getNameNode(); + PsiElement markable = node; + if (nameNode != null) { + markable = nameNode.getPsi(); + } + return markable; + } - private void checkGetter(PyCallable callable, PsiElement beingChecked) { - if (callable != null) { - checkOneParameter(callable, beingChecked, true); - checkReturnValueAllowed(callable, beingChecked, true, PyBundle.message("INSP.getter.return.smth")); - } - } - private void checkSetter(PyCallable callable, PsiElement beingChecked) { - if (callable != null) { - // signature: at least two params, more optionals ok; first arg 'self' - final PyParameterList paramList = callable.getParameterList(); - if (myTwoParamFunction != null && !PyUtil.isSignatureCompatibleTo(callable, myTwoParamFunction, myTypeEvalContext)) { - registerProblem(beingChecked, PyBundle.message("INSP.setter.signature.advice"), new PyUpdatePropertySignatureQuickFix(true)); + private void checkGetter(PyCallable callable, PsiElement beingChecked) { + if (callable != null) { + checkOneParameter(callable, beingChecked, true); + checkReturnValueAllowed(callable, beingChecked, true, PyLocalize.inspGetterReturnSmth().get()); + } } - checkForSelf(paramList); - // no explicit return type - checkReturnValueAllowed(callable, beingChecked, false, PyBundle.message("INSP.setter.should.not.return")); - } - } - private void checkDeleter(PyCallable callable, PsiElement beingChecked) { - if (callable != null) { - checkOneParameter(callable, beingChecked, false); - checkReturnValueAllowed(callable, beingChecked, false, PyBundle.message("INSP.deleter.should.not.return")); - } - } + private void checkSetter(PyCallable callable, PsiElement beingChecked) { + if (callable != null) { + // signature: at least two params, more optionals ok; first arg 'self' + final PyParameterList paramList = callable.getParameterList(); + if (myTwoParamFunction != null && !PyUtil.isSignatureCompatibleTo(callable, myTwoParamFunction, myTypeEvalContext)) { + registerProblem( + beingChecked, + PyLocalize.inspSetterSignatureAdvice().get(), + new PyUpdatePropertySignatureQuickFix(true) + ); + } + checkForSelf(paramList); + // no explicit return type + checkReturnValueAllowed(callable, beingChecked, false, PyLocalize.inspSetterShouldNotReturn().get()); + } + } - private void checkOneParameter(PyCallable callable, PsiElement beingChecked, boolean isGetter) { - final PyParameterList parameterList = callable.getParameterList(); - if (myOneParamFunction != null && !PyUtil.isSignatureCompatibleTo(callable, myOneParamFunction, myTypeEvalContext)) { - if (isGetter) { - registerProblem(beingChecked, PyBundle.message("INSP.getter.signature.advice"), new PyUpdatePropertySignatureQuickFix(false)); + private void checkDeleter(PyCallable callable, PsiElement beingChecked) { + if (callable != null) { + checkOneParameter(callable, beingChecked, false); + checkReturnValueAllowed(callable, beingChecked, false, PyLocalize.inspDeleterShouldNotReturn().get()); + } } - else { - registerProblem(beingChecked, PyBundle.message("INSP.deleter.signature.advice"), new PyUpdatePropertySignatureQuickFix(false)); + + private void checkOneParameter(PyCallable callable, PsiElement beingChecked, boolean isGetter) { + final PyParameterList parameterList = callable.getParameterList(); + if (myOneParamFunction != null && !PyUtil.isSignatureCompatibleTo(callable, myOneParamFunction, myTypeEvalContext)) { + if (isGetter) { + registerProblem( + beingChecked, + PyLocalize.inspGetterSignatureAdvice().get(), + new PyUpdatePropertySignatureQuickFix(false) + ); + } + else { + registerProblem( + beingChecked, + PyLocalize.inspDeleterSignatureAdvice().get(), + new PyUpdatePropertySignatureQuickFix(false) + ); + } + } + checkForSelf(parameterList); } - } - checkForSelf(parameterList); - } - private void checkForSelf(PyParameterList paramList) { - PyParameter[] parameters = paramList.getParameters(); - final PyClass cls = PsiTreeUtil.getParentOfType(paramList, PyClass.class); - if (cls != null && cls.isSubclass("type", myTypeEvalContext)) { - return; - } - if (parameters.length > 0 && !PyNames.CANONICAL_SELF.equals(parameters[0].getName())) { - registerProblem(parameters[0], - PyBundle.message("INSP.accessor.first.param.is.$0", PyNames.CANONICAL_SELF), - ProblemHighlightType.WEAK_WARNING, - null, - new RenameParameterQuickFix - (PyNames.CANONICAL_SELF)); - } - } + private void checkForSelf(PyParameterList paramList) { + PyParameter[] parameters = paramList.getParameters(); + final PyClass cls = PsiTreeUtil.getParentOfType(paramList, PyClass.class); + if (cls != null && cls.isSubclass("type", myTypeEvalContext)) { + return; + } + if (parameters.length > 0 && !PyNames.CANONICAL_SELF.equals(parameters[0].getName())) { + registerProblem( + parameters[0], + PyLocalize.inspAccessorFirstParamIs$0(PyNames.CANONICAL_SELF).get(), + ProblemHighlightType.WEAK_WARNING, + null, + new RenameParameterQuickFix(PyNames.CANONICAL_SELF) + ); + } + } - private void checkReturnValueAllowed(@Nonnull PyCallable callable, - @Nonnull PsiElement beingChecked, - boolean allowed, - @Nonnull String message) { - if (callable instanceof PyFunction) { - final PyFunction function = (PyFunction)callable; + private void checkReturnValueAllowed( + @Nonnull PyCallable callable, + @Nonnull PsiElement beingChecked, + boolean allowed, + @Nonnull String message + ) { + if (callable instanceof PyFunction) { + final PyFunction function = (PyFunction) callable; - if (PyUtil.isDecoratedAsAbstract(function)) { - return; - } + if (PyUtil.isDecoratedAsAbstract(function)) { + return; + } - if (allowed && !someFlowHasExitPoint(function, Visitor::isAllowedExitPoint) || !allowed && someFlowHasExitPoint(function, - Visitor::isDisallowedExitPoint)) { - registerProblem(beingChecked, message); - } - } - else { - final PyType type = myTypeEvalContext.getReturnType(callable); - final boolean hasReturns = !(type instanceof PyNoneType); + if (allowed && !someFlowHasExitPoint(function, Visitor::isAllowedExitPoint) || !allowed && someFlowHasExitPoint( + function, + Visitor::isDisallowedExitPoint + )) { + registerProblem(beingChecked, message); + } + } + else { + final PyType type = myTypeEvalContext.getReturnType(callable); + final boolean hasReturns = !(type instanceof PyNoneType); - if (allowed ^ hasReturns) { - registerProblem(beingChecked, message); + if (allowed ^ hasReturns) { + registerProblem(beingChecked, message); + } + } } - } - } - private static boolean someFlowHasExitPoint(@Nonnull PyFunction function, @Nonnull Predicate exitPointPredicate) { - final Ref result = new Ref<>(false); + private static boolean someFlowHasExitPoint(@Nonnull PyFunction function, @Nonnull Predicate exitPointPredicate) { + final Ref result = new Ref<>(false); - ControlFlowUtil.process(ControlFlowCache.getControlFlow(function).getInstructions(), - 0, - instruction -> { - result.set(exitPointPredicate.test(instruction.getElement())); - return !result.get(); - }); + ControlFlowUtil.process( + ControlFlowCache.getControlFlow(function).getInstructions(), + 0, + instruction -> { + result.set(exitPointPredicate.test(instruction.getElement())); + return !result.get(); + } + ); - return result.get(); - } + return result.get(); + } - private static boolean isAllowedExitPoint(@Nullable PsiElement element) { - return element instanceof PyRaiseStatement || element instanceof PyReturnStatement || element instanceof PyYieldExpression; - } + private static boolean isAllowedExitPoint(@Nullable PsiElement element) { + return element instanceof PyRaiseStatement || element instanceof PyReturnStatement || element instanceof PyYieldExpression; + } - private static boolean isDisallowedExitPoint(@Nullable PsiElement element) { - return element instanceof PyReturnStatement && ((PyReturnStatement)element).getExpression() != null || element instanceof PyYieldExpression; + private static boolean isDisallowedExitPoint(@Nullable PsiElement element) { + return element instanceof PyReturnStatement && ((PyReturnStatement) element).getExpression() != null || element instanceof PyYieldExpression; + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyRaisingNewStyleClassInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyRaisingNewStyleClassInspection.java index 420c43d5..69d2f2c6 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyRaisingNewStyleClassInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyRaisingNewStyleClassInspection.java @@ -15,15 +15,14 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.psi.*; 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; import jakarta.annotation.Nullable; @@ -32,48 +31,49 @@ */ @ExtensionImpl public class PyRaisingNewStyleClassInspection extends PyInspection { - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.raising.new.style.class"); - } - - @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.inspNameRaisingNewStyleClass(); } + @Nonnull @Override - public void visitPyRaiseStatement(PyRaiseStatement node) { - if (LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON25)) { - return; - } - final PyExpression[] expressions = node.getExpressions(); - if (expressions.length == 0) { - return; - } - final PyExpression expression = expressions[0]; - if (expression instanceof PyCallExpression) { - final PyExpression callee = ((PyCallExpression)expression).getCallee(); - if (callee instanceof PyReferenceExpression) { - final PsiElement psiElement = ((PyReferenceExpression)callee).getReference(getResolveContext()).resolve(); - if (psiElement instanceof PyClass) { - if (((PyClass)psiElement).isNewStyleClass(null)) { - registerProblem(expression, "Raising a new style class"); + 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) { + if (LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON25)) { + return; + } + final PyExpression[] expressions = node.getExpressions(); + if (expressions.length == 0) { + return; + } + final PyExpression expression = expressions[0]; + if (expression instanceof PyCallExpression) { + final PyExpression callee = ((PyCallExpression) expression).getCallee(); + if (callee instanceof PyReferenceExpression) { + final PsiElement psiElement = ((PyReferenceExpression) callee).getReference(getResolveContext()).resolve(); + if (psiElement instanceof PyClass) { + if (((PyClass) psiElement).isNewStyleClass(null)) { + registerProblem(expression, "Raising a new style class"); + } + } + } } - } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyRedeclarationInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyRedeclarationInspection.java index 0662b88d..f8b96dff 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyRedeclarationInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyRedeclarationInspection.java @@ -13,12 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.inspections; import com.jetbrains.python.PyNames; 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.ScopeUtil; @@ -34,11 +32,12 @@ import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiNameIdentifierOwner; import consulo.language.psi.util.PsiTreeUtil; +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 jakarta.annotation.Nullable; + import java.util.ArrayList; import java.util.List; import java.util.function.Function; @@ -51,136 +50,146 @@ */ @ExtensionImpl public class PyRedeclarationInspection extends PyInspection { - @Nls - @Nonnull - public String getDisplayName() { - return PyBundle.message("INSP.NAME.redeclaration"); - } - - @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 void visitPyFunction(final PyFunction node) { - if (!isDecorated(node)) { - processElement(node); - } + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameRedeclaration(); } + @Nonnull @Override - public void visitPyTargetExpression(final PyTargetExpression node) { - final ScopeOwner owner = ScopeUtil.getScopeOwner(node); - if (owner instanceof PyFile || owner instanceof PyClass) { - processElement(node); - } + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } - @Override - public void visitPyClass(final PyClass node) { - if (!isDecorated(node)) { - processElement(node); - } - } + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } - private static boolean isConditional(@Nonnull PsiElement node) { - return PsiTreeUtil.getParentOfType(node, PyIfStatement.class, PyConditionalExpression.class, PyTryExceptStatement.class) != null; - } + @Override + public void visitPyFunction(final PyFunction node) { + if (!isDecorated(node)) { + processElement(node); + } + } - private static boolean isDecorated(@Nonnull PyDecoratable node) { - boolean isDecorated = false; - final PyDecoratorList decoratorList = node.getDecoratorList(); - if (decoratorList != null) { - final PyDecorator[] decorators = decoratorList.getDecorators(); - if (decorators.length > 0) { - isDecorated = true; + @Override + public void visitPyTargetExpression(final PyTargetExpression node) { + final ScopeOwner owner = ScopeUtil.getScopeOwner(node); + if (owner instanceof PyFile || owner instanceof PyClass) { + processElement(node); + } } - } - return isDecorated; - } - private void processElement(@Nonnull final PsiNameIdentifierOwner element) { - if (isConditional(element)) { - return; - } - final String name = element.getName(); - final ScopeOwner owner = ScopeUtil.getScopeOwner(element); - if (owner != null && name != null) { - final Instruction[] instructions = ControlFlowCache.getControlFlow(owner).getInstructions(); - PsiElement elementInControlFlow = element; - if (element instanceof PyTargetExpression) { - final PyImportStatement importStatement = PsiTreeUtil.getParentOfType(element, PyImportStatement.class); - if (importStatement != null) { - elementInControlFlow = importStatement; - } + @Override + public void visitPyClass(final PyClass node) { + if (!isDecorated(node)) { + processElement(node); + } + } + + private static boolean isConditional(@Nonnull PsiElement node) { + return PsiTreeUtil.getParentOfType( + node, + PyIfStatement.class, + PyConditionalExpression.class, + PyTryExceptStatement.class + ) != null; } - final int startInstruction = ControlFlowUtil.findInstructionNumberByElement(instructions, elementInControlFlow); - if (startInstruction < 0) { - return; + + private static boolean isDecorated(@Nonnull PyDecoratable node) { + boolean isDecorated = false; + final PyDecoratorList decoratorList = node.getDecoratorList(); + if (decoratorList != null) { + final PyDecorator[] decorators = decoratorList.getDecorators(); + if (decorators.length > 0) { + isDecorated = true; + } + } + return isDecorated; } - final Ref readElementRef = Ref.create(null); - final Ref writeElementRef = Ref.create(null); - ControlFlowUtil.iteratePrev(startInstruction, - instructions, new Function() { - @Override - public ControlFlowUtil.Operation apply(Instruction instruction) { - if (instruction instanceof ReadWriteInstruction && instruction.num() != startInstruction) { - final ReadWriteInstruction rwInstruction = (ReadWriteInstruction)instruction; - if (name.equals(rwInstruction.getName())) { - final PsiElement originalElement = rwInstruction.getElement(); - if (originalElement != null) { - if (rwInstruction.getAccess().isReadAccess()) { - readElementRef.set(originalElement); + + private void processElement(@Nonnull final PsiNameIdentifierOwner element) { + if (isConditional(element)) { + return; + } + final String name = element.getName(); + final ScopeOwner owner = ScopeUtil.getScopeOwner(element); + if (owner != null && name != null) { + final Instruction[] instructions = ControlFlowCache.getControlFlow(owner).getInstructions(); + PsiElement elementInControlFlow = element; + if (element instanceof PyTargetExpression) { + final PyImportStatement importStatement = PsiTreeUtil.getParentOfType(element, PyImportStatement.class); + if (importStatement != null) { + elementInControlFlow = importStatement; } - if (rwInstruction.getAccess().isWriteAccess()) { - if (originalElement != element) { - writeElementRef.set(originalElement); - } + } + final int startInstruction = ControlFlowUtil.findInstructionNumberByElement(instructions, elementInControlFlow); + if (startInstruction < 0) { + return; + } + final Ref readElementRef = Ref.create(null); + final Ref writeElementRef = Ref.create(null); + ControlFlowUtil.iteratePrev(startInstruction, + instructions, new Function() { + @Override + public ControlFlowUtil.Operation apply(Instruction instruction) { + if (instruction instanceof ReadWriteInstruction && instruction.num() != startInstruction) { + final ReadWriteInstruction rwInstruction = (ReadWriteInstruction) instruction; + if (name.equals(rwInstruction.getName())) { + final PsiElement originalElement = rwInstruction.getElement(); + if (originalElement != null) { + if (rwInstruction.getAccess().isReadAccess()) { + readElementRef.set(originalElement); + } + if (rwInstruction.getAccess().isWriteAccess()) { + if (originalElement != element) { + writeElementRef.set(originalElement); + } + } + } + return ControlFlowUtil.Operation.CONTINUE; + } + } + return ControlFlowUtil.Operation.NEXT; + } } - } - return ControlFlowUtil.Operation.CONTINUE; + ); + final PsiElement writeElement = writeElementRef.get(); + if (writeElement != null && readElementRef.get() == null) { + final List quickFixes = new ArrayList(); + if (suggestRename(element, writeElement)) { + quickFixes.add(new PyRenameElementQuickFix()); + } + final PsiElement identifier = element.getNameIdentifier(); + registerProblem( + identifier != null ? identifier : element, + PyLocalize.inspRedeclaredName(name).get(), + ProblemHighlightType.GENERIC_ERROR_OR_WARNING, + null, + quickFixes.toArray(new LocalQuickFix[quickFixes.size()]) + ); } - } - return ControlFlowUtil.Operation.NEXT; } - }); - final PsiElement writeElement = writeElementRef.get(); - if (writeElement != null && readElementRef.get() == null) { - final List quickFixes = new ArrayList(); - if (suggestRename(element, writeElement)) { - quickFixes.add(new PyRenameElementQuickFix()); - } - final PsiElement identifier = element.getNameIdentifier(); - registerProblem(identifier != null ? identifier : element, - PyBundle.message("INSP.redeclared.name", name), - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - null, - quickFixes.toArray(new LocalQuickFix[quickFixes.size()])); } - } - } - private static boolean suggestRename(@Nonnull PsiNameIdentifierOwner element, @Nonnull PsiElement originalElement) { - // Target expressions in the same scope are treated as the same variable - if ((element instanceof PyTargetExpression) && originalElement instanceof PyTargetExpression) { - return false; - } - // Renaming an __init__ method results in renaming its class - else if (element instanceof PyFunction && PyNames.INIT.equals(element.getName()) && - ((PyFunction)element).getContainingClass() != null) { - return false; - } - return true; + private static boolean suggestRename(@Nonnull PsiNameIdentifierOwner element, @Nonnull PsiElement originalElement) { + // Target expressions in the same scope are treated as the same variable + if ((element instanceof PyTargetExpression) && originalElement instanceof PyTargetExpression) { + return false; + } + // Renaming an __init__ method results in renaming its class + else if (element instanceof PyFunction && PyNames.INIT.equals(element.getName()) && + ((PyFunction) element).getContainingClass() != null) { + return false; + } + return true; + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyRedundantParenthesesInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyRedundantParenthesesInspection.java index 70079119..41777079 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyRedundantParenthesesInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyRedundantParenthesesInspection.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.RedundantParenthesesQuickFix; import com.jetbrains.python.psi.*; @@ -27,99 +25,102 @@ import consulo.language.psi.PsiElement; 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; /** - * User: catherine - *

* Inspection to detect redundant parentheses in if/while statement. + * + * @author catherine */ @ExtensionImpl public class PyRedundantParenthesesInspection extends PyInspection { - @Nonnull - @Override - public InspectionToolState createStateProvider() { - return new PyRedundantParenthesesInspectionState(); - } + @Nonnull + @Override + public InspectionToolState createStateProvider() { + return new PyRedundantParenthesesInspectionState(); + } - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.redundant.parentheses"); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameRedundantParentheses(); + } - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - PyRedundantParenthesesInspectionState inspectionState = (PyRedundantParenthesesInspectionState)state; - return new Visitor(holder, session, inspectionState.myIgnorePercOperator, inspectionState.myIgnoreTupleInReturn); - } + @Nonnull + @Override + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + PyRedundantParenthesesInspectionState inspectionState = (PyRedundantParenthesesInspectionState) state; + return new Visitor(holder, session, inspectionState.myIgnorePercOperator, inspectionState.myIgnoreTupleInReturn); + } - private static class Visitor extends PyInspectionVisitor { - private final boolean myIgnorePercOperator; - private final boolean myIgnoreTupleInReturn; + private static class Visitor extends PyInspectionVisitor { + private final boolean myIgnorePercOperator; + private final boolean myIgnoreTupleInReturn; - public Visitor(@Nonnull ProblemsHolder holder, - @Nonnull LocalInspectionToolSession session, - boolean ignorePercOperator, - boolean ignoreTupleInReturn) { - super(holder, session); - myIgnorePercOperator = ignorePercOperator; - myIgnoreTupleInReturn = ignoreTupleInReturn; - } + public Visitor( + @Nonnull ProblemsHolder holder, + @Nonnull LocalInspectionToolSession session, + boolean ignorePercOperator, + boolean ignoreTupleInReturn + ) { + super(holder, session); + myIgnorePercOperator = ignorePercOperator; + myIgnoreTupleInReturn = ignoreTupleInReturn; + } - @Override - public void visitPyParenthesizedExpression(final PyParenthesizedExpression node) { - PyExpression expression = node.getContainedExpression(); - if (node.getText().contains("\n")) { - return; - } - PyYieldExpression yieldExpression = PsiTreeUtil.getParentOfType(expression, PyYieldExpression.class, false); - if (yieldExpression != null) { - return; - } - if (expression instanceof PyReferenceExpression || expression instanceof PyLiteralExpression) { - if (myIgnorePercOperator) { - PsiElement parent = node.getParent(); - if (parent instanceof PyBinaryExpression) { - if (((PyBinaryExpression)parent).getOperator() == PyTokenTypes.PERC) { - return; + @Override + public void visitPyParenthesizedExpression(final PyParenthesizedExpression node) { + PyExpression expression = node.getContainedExpression(); + if (node.getText().contains("\n")) { + return; } - } - } + PyYieldExpression yieldExpression = PsiTreeUtil.getParentOfType(expression, PyYieldExpression.class, false); + if (yieldExpression != null) { + return; + } + if (expression instanceof PyReferenceExpression || expression instanceof PyLiteralExpression) { + if (myIgnorePercOperator) { + PsiElement parent = node.getParent(); + if (parent instanceof PyBinaryExpression) { + if (((PyBinaryExpression) parent).getOperator() == PyTokenTypes.PERC) { + return; + } + } + } - if (node.getParent() instanceof PyPrintStatement) { - return; - } - registerProblem(node, "Remove redundant parentheses", new RedundantParenthesesQuickFix()); - } - else if (node.getParent() instanceof PyReturnStatement && expression instanceof PyTupleExpression && myIgnoreTupleInReturn) { - return; - } - else if (node.getParent() instanceof PyIfPart || node.getParent() instanceof PyWhilePart - || node.getParent() instanceof PyReturnStatement) { - registerProblem(node, "Remove redundant parentheses", new RedundantParenthesesQuickFix()); - } - else if (expression instanceof PyBinaryExpression) { - PyBinaryExpression binaryExpression = (PyBinaryExpression)expression; + if (node.getParent() instanceof PyPrintStatement) { + return; + } + registerProblem(node, "Remove redundant parentheses", new RedundantParenthesesQuickFix()); + } + else if (node.getParent() instanceof PyReturnStatement && expression instanceof PyTupleExpression && myIgnoreTupleInReturn) { + return; + } + else if (node.getParent() instanceof PyIfPart || node.getParent() instanceof PyWhilePart + || node.getParent() instanceof PyReturnStatement) { + registerProblem(node, "Remove redundant parentheses", new RedundantParenthesesQuickFix()); + } + else if (expression instanceof PyBinaryExpression) { + PyBinaryExpression binaryExpression = (PyBinaryExpression) expression; - if (node.getParent() instanceof PyPrefixExpression) { - return; - } - if (binaryExpression.getOperator() == PyTokenTypes.AND_KEYWORD || - binaryExpression.getOperator() == PyTokenTypes.OR_KEYWORD) { - if (binaryExpression.getLeftExpression() instanceof PyParenthesizedExpression && - binaryExpression.getRightExpression() instanceof PyParenthesizedExpression) { - registerProblem(node, "Remove redundant parentheses", new RedundantParenthesesQuickFix()); - } + if (node.getParent() instanceof PyPrefixExpression) { + return; + } + if (binaryExpression.getOperator() == PyTokenTypes.AND_KEYWORD || + binaryExpression.getOperator() == PyTokenTypes.OR_KEYWORD) { + if (binaryExpression.getLeftExpression() instanceof PyParenthesizedExpression && + binaryExpression.getRightExpression() instanceof PyParenthesizedExpression) { + registerProblem(node, "Remove redundant parentheses", new RedundantParenthesesQuickFix()); + } + } + } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyReturnFromInitInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyReturnFromInitInspection.java index b3804db9..79a007b2 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyReturnFromInitInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyReturnFromInitInspection.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.PyNames; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFunction; @@ -26,58 +24,66 @@ 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; + import java.util.ArrayList; import java.util.Collection; /** * Checks that no value is returned from __init__(). - * User: dcheryasov - * Date: Nov 12, 2009 10:20:49 PM + * + * @author dcheryasov + * @since 2009-11-12 */ @ExtensionImpl public class PyReturnFromInitInspection extends PyInspection { - @Nls - @Nonnull - public String getDisplayName() { - return PyBundle.message("INSP.NAME.init.return"); - } - - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - return new Visitor(holder, session); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameInitReturn(); + } - public static class Visitor extends PyInspectionVisitor { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { - super(holder, session); + @Nonnull + @Override + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } - public void visitPyFunction(PyFunction function) { - if (function.getContainingClass() != null && PyNames.INIT.equals(function.getName())) { - Collection offenders = new ArrayList(); - findReturnValueInside(function, offenders); - for (PsiElement offender : offenders) { - registerProblem(offender, PyBundle.message("INSP.cant.return.value.from.init")); + public static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } + + public void visitPyFunction(PyFunction function) { + if (function.getContainingClass() != null && PyNames.INIT.equals(function.getName())) { + Collection offenders = new ArrayList(); + findReturnValueInside(function, offenders); + for (PsiElement offender : offenders) { + registerProblem(offender, PyLocalize.inspCantReturnValueFromInit().get()); + } + } } - } - } - private static void findReturnValueInside(@Nonnull PsiElement node, Collection offenders) { - for (PsiElement child = node.getFirstChild(); child != null; child = child.getNextSibling()) { - if (child instanceof PyFunction || child instanceof PyClass) continue; // ignore possible inner functions and classes - if (child instanceof PyReturnStatement) { - if (((PyReturnStatement)child).getExpression() != null) offenders.add(child); + private static void findReturnValueInside(@Nonnull PsiElement node, Collection offenders) { + for (PsiElement child = node.getFirstChild(); child != null; child = child.getNextSibling()) { + if (child instanceof PyFunction || child instanceof PyClass) { + continue; // ignore possible inner functions and classes + } + if (child instanceof PyReturnStatement) { + if (((PyReturnStatement) child).getExpression() != null) { + offenders.add(child); + } + } + findReturnValueInside(child, offenders); + } } - findReturnValueInside(child, offenders); - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySetFunctionToLiteralInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySetFunctionToLiteralInspection.java index 6b075336..debe78c9 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySetFunctionToLiteralInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySetFunctionToLiteralInspection.java @@ -15,11 +15,10 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.impl.inspections.quickfix.ReplaceFunctionWithSetLiteralQuickFix; -import com.jetbrains.python.psi.*; import com.jetbrains.python.impl.psi.impl.PyBuiltinCache; +import com.jetbrains.python.psi.*; import consulo.annotation.component.ExtensionImpl; import consulo.language.editor.inspection.LocalInspectionToolSession; import consulo.language.editor.inspection.ProblemsHolder; @@ -27,114 +26,99 @@ import consulo.language.editor.inspection.scheme.InspectionProjectProfileManager; import consulo.language.editor.inspection.scheme.InspectionToolWrapper; 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; + import java.util.List; /** - * User: catherine - *

* Inspection to find set built-in function and replace it with set literal * available if the selected language level supports set literals. + * + * @author catherine */ @ExtensionImpl -public class PySetFunctionToLiteralInspection extends PyInspection -{ - - @Nls - @Nonnull - @Override - public String getDisplayName() - { - return PyBundle.message("INSP.NAME.set.function.to.literal"); - } +public class PySetFunctionToLiteralInspection extends PyInspection { + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameSetFunctionToLiteral(); + } - @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 - { - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) - { - super(holder, session); - } + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } - @Override - public void visitPyCallExpression(final PyCallExpression node) - { - if(!isAvailable(node)) - { - return; - } - PyExpression callee = node.getCallee(); - if(node.isCalleeText(PyNames.SET) && callee != null && PyBuiltinCache.isInBuiltins(callee)) - { - PyExpression[] arguments = node.getArguments(); - if(arguments.length == 1) - { - PyElement[] elements = getSetCallArguments(node); - if(elements.length != 0) - { - registerProblem(node, PyBundle.message("INSP.NAME.set.function.to.literal"), new ReplaceFunctionWithSetLiteralQuickFix()); - } - } - } - } + @Override + public void visitPyCallExpression(final PyCallExpression node) { + if (!isAvailable(node)) { + return; + } + PyExpression callee = node.getCallee(); + if (node.isCalleeText(PyNames.SET) && callee != null && PyBuiltinCache.isInBuiltins(callee)) { + PyExpression[] arguments = node.getArguments(); + if (arguments.length == 1) { + PyElement[] elements = getSetCallArguments(node); + if (elements.length != 0) { + registerProblem( + node, + PyLocalize.inspNameSetFunctionToLiteral().get(), + new ReplaceFunctionWithSetLiteralQuickFix() + ); + } + } + } + } - private static boolean isAvailable(PyCallExpression node) - { - final InspectionProfile profile = InspectionProjectProfileManager.getInstance(node.getProject()).getInspectionProfile(); - final InspectionToolWrapper inspectionTool = profile.getInspectionTool("PyCompatibilityInspection", node.getProject()); - if(inspectionTool != null) - { - final Object inspection = inspectionTool.getState(); - if(inspection instanceof PyCompatibilityInspectionState) - { - final List versions = ((PyCompatibilityInspectionState) inspection).versions; - for(String s : versions) - { - if(!LanguageLevel.fromPythonVersion(s).supportsSetLiterals()) - { - return false; - } - } - } - } - return LanguageLevel.forElement(node).supportsSetLiterals(); - } - } + private static boolean isAvailable(PyCallExpression node) { + final InspectionProfile profile = InspectionProjectProfileManager.getInstance(node.getProject()).getInspectionProfile(); + final InspectionToolWrapper inspectionTool = profile.getInspectionTool("PyCompatibilityInspection", node.getProject()); + if (inspectionTool != null) { + final Object inspection = inspectionTool.getState(); + if (inspection instanceof PyCompatibilityInspectionState) { + final List versions = ((PyCompatibilityInspectionState) inspection).versions; + for (String s : versions) { + if (!LanguageLevel.fromPythonVersion(s).supportsSetLiterals()) { + return false; + } + } + } + } + return LanguageLevel.forElement(node).supportsSetLiterals(); + } + } - public static PyElement[] getSetCallArguments(PyCallExpression node) - { - PyExpression argument = node.getArguments()[0]; - if(argument instanceof PyStringLiteralExpression) - { - return PyElement.EMPTY_ARRAY; - } - if((argument instanceof PySequenceExpression || (argument instanceof PyParenthesizedExpression && ((PyParenthesizedExpression) argument) - .getContainedExpression() instanceof - PyTupleExpression))) - { + public static PyElement[] getSetCallArguments(PyCallExpression node) { + PyExpression argument = node.getArguments()[0]; + if (argument instanceof PyStringLiteralExpression) { + return PyElement.EMPTY_ARRAY; + } + if ((argument instanceof PySequenceExpression || (argument instanceof PyParenthesizedExpression && ((PyParenthesizedExpression) argument) + .getContainedExpression() instanceof + PyTupleExpression))) { - if(argument instanceof PySequenceExpression) - { - return ((PySequenceExpression) argument).getElements(); - } - PyExpression tuple = ((PyParenthesizedExpression) argument).getContainedExpression(); - if(tuple instanceof PyTupleExpression) - { - return ((PyTupleExpression) (tuple)).getElements(); - } - } - return PyElement.EMPTY_ARRAY; - } + if (argument instanceof PySequenceExpression) { + return ((PySequenceExpression) argument).getElements(); + } + PyExpression tuple = ((PyParenthesizedExpression) argument).getContainedExpression(); + if (tuple instanceof PyTupleExpression) { + return ((PyTupleExpression) (tuple)).getElements(); + } + } + return PyElement.EMPTY_ARRAY; + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyShadowingNamesInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyShadowingNamesInspection.java index beca9977..648adfcb 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyShadowingNamesInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PyShadowingNamesInspection.java @@ -15,14 +15,14 @@ */ package com.jetbrains.python.impl.inspections; -import com.jetbrains.python.impl.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; +import com.jetbrains.python.impl.codeInsight.controlflow.ControlFlowCache; import com.jetbrains.python.impl.codeInsight.dataflow.scope.Scope; import com.jetbrains.python.impl.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.impl.psi.PyUtil; -import com.jetbrains.python.psi.*; import com.jetbrains.python.impl.psi.resolve.PyResolveProcessor; import com.jetbrains.python.impl.psi.resolve.PyResolveUtil; +import com.jetbrains.python.psi.*; import consulo.annotation.component.ExtensionImpl; import consulo.language.editor.inspection.LocalInspectionToolSession; import consulo.language.editor.inspection.ProblemHighlightType; @@ -31,7 +31,7 @@ import consulo.language.psi.PsiElementVisitor; import consulo.language.psi.PsiNameIdentifierOwner; import consulo.language.psi.util.PsiTreeUtil; - +import consulo.localize.LocalizeValue; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -42,90 +42,95 @@ */ @ExtensionImpl public class PyShadowingNamesInspection extends PyInspection { - @Nonnull - @Override - public String getDisplayName() { - return "Shadowing names from outer scopes"; - } - - @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 void visitPyClass(@Nonnull PyClass node) { - processElement(node); + public LocalizeValue getDisplayName() { + return LocalizeValue.localizeTODO("Shadowing names from outer scopes"); } + @Nonnull @Override - public void visitPyFunction(@Nonnull PyFunction node) { - processElement(node); + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + return new Visitor(holder, session); } - @Override - public void visitPyNamedParameter(@Nonnull PyNamedParameter node) { - if (node.isSelf()) { - return; - } - processElement(node); - } + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session) { + super(holder, session); + } - @Override - public void visitPyTargetExpression(@Nonnull PyTargetExpression node) { - if (!node.isQualified()) { - processElement(node); - } - } + @Override + public void visitPyClass(@Nonnull PyClass node) { + processElement(node); + } - private void processElement(@Nonnull PsiNameIdentifierOwner element) { - final ScopeOwner owner = ScopeUtil.getScopeOwner(element); - if (owner instanceof PyClass) { - return; - } - final String name = element.getName(); - if (name != null) { - final PsiElement identifier = element.getNameIdentifier(); - final PsiElement problemElement = identifier != null ? identifier : element; - if ("_".equals(name)) { - return; + @Override + public void visitPyFunction(@Nonnull PyFunction node) { + processElement(node); } - if (owner != null) { - final ScopeOwner nextOwner = ScopeUtil.getScopeOwner(owner); - if (nextOwner != null) { - final PyResolveProcessor processor = new PyResolveProcessor(name); - PyResolveUtil.scopeCrawlUp(processor, nextOwner, null, name, null); - for (PsiElement resolved : processor.getElements()) { - if (resolved != null) { - final PyComprehensionElement comprehension = PsiTreeUtil.getParentOfType(resolved, PyComprehensionElement.class); - if (comprehension != null && PyUtil.isOwnScopeComprehension(comprehension)) { - return; + + @Override + public void visitPyNamedParameter(@Nonnull PyNamedParameter node) { + if (node.isSelf()) { + return; + } + processElement(node); + } + + @Override + public void visitPyTargetExpression(@Nonnull PyTargetExpression node) { + if (!node.isQualified()) { + processElement(node); + } + } + + private void processElement(@Nonnull PsiNameIdentifierOwner element) { + final ScopeOwner owner = ScopeUtil.getScopeOwner(element); + if (owner instanceof PyClass) { + return; + } + final String name = element.getName(); + if (name != null) { + final PsiElement identifier = element.getNameIdentifier(); + final PsiElement problemElement = identifier != null ? identifier : element; + if ("_".equals(name)) { + return; } - final Scope scope = ControlFlowCache.getScope(owner); - if (scope.isGlobal(name) || scope.isNonlocal(name)) { - return; + if (owner != null) { + final ScopeOwner nextOwner = ScopeUtil.getScopeOwner(owner); + if (nextOwner != null) { + final PyResolveProcessor processor = new PyResolveProcessor(name); + PyResolveUtil.scopeCrawlUp(processor, nextOwner, null, name, null); + for (PsiElement resolved : processor.getElements()) { + if (resolved != null) { + final PyComprehensionElement comprehension = + PsiTreeUtil.getParentOfType(resolved, PyComprehensionElement.class); + if (comprehension != null && PyUtil.isOwnScopeComprehension(comprehension)) { + return; + } + final Scope scope = ControlFlowCache.getScope(owner); + if (scope.isGlobal(name) || scope.isNonlocal(name)) { + return; + } + registerProblem( + problemElement, + String.format("Shadows name '%s' from outer scope", name), + ProblemHighlightType.WEAK_WARNING, + null, + new PyRenameElementQuickFix() + ); + return; + } + } + } } - registerProblem(problemElement, - String.format("Shadows name '%s' from outer scope", name), - ProblemHighlightType.WEAK_WARNING, - null, - new PyRenameElementQuickFix()); - return; - } } - } } - } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySimplifyBooleanCheckInspection.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySimplifyBooleanCheckInspection.java index dee1fca9..67a538b4 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySimplifyBooleanCheckInspection.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/PySimplifyBooleanCheckInspection.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.google.common.collect.ImmutableList; -import com.jetbrains.python.impl.PyBundle; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.impl.inspections.quickfix.SimplifyBooleanCheckQuickFix; import com.jetbrains.python.psi.PyBinaryExpression; @@ -29,10 +27,11 @@ 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; + import java.util.Collection; import java.util.Collections; import java.util.List; @@ -42,92 +41,97 @@ */ @ExtensionImpl public class PySimplifyBooleanCheckInspection extends PyInspection { - private static List COMPARISON_LITERALS = ImmutableList.of("True", "False", "[]"); - - @Nonnull - @Override - public InspectionToolState createStateProvider() { - return new PySimplifyBooleanCheckInspectionState(); - } - - @Nls - @Nonnull - @Override - public String getDisplayName() { - return PyBundle.message("INSP.NAME.check.can.be.simplified"); - } + private static List COMPARISON_LITERALS = ImmutableList.of("True", "False", "[]"); - @Nonnull - @Override - public PsiElementVisitor buildVisitor(@Nonnull ProblemsHolder holder, - boolean isOnTheFly, - @Nonnull LocalInspectionToolSession session, - Object state) { - PySimplifyBooleanCheckInspectionState inspectionState = (PySimplifyBooleanCheckInspectionState)state; - return new Visitor(holder, session, inspectionState.ignoreComparisonToZero); - } - - private static class Visitor extends PyInspectionVisitor { - private final boolean myIgnoreComparisonToZero; + @Nonnull + @Override + public InspectionToolState createStateProvider() { + return new PySimplifyBooleanCheckInspectionState(); + } - public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session, boolean ignoreComparisonToZero) { - super(holder, session); - myIgnoreComparisonToZero = ignoreComparisonToZero; + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PyLocalize.inspNameCheckCanBeSimplified(); } + @Nonnull @Override - public void visitPyConditionalStatementPart(PyConditionalStatementPart node) { - super.visitPyConditionalStatementPart(node); - final PyExpression condition = node.getCondition(); - if (condition != null) { - condition.accept(new PyBinaryExpressionVisitor(getHolder(), getSession(), myIgnoreComparisonToZero)); - } + public PsiElementVisitor buildVisitor( + @Nonnull ProblemsHolder holder, + boolean isOnTheFly, + @Nonnull LocalInspectionToolSession session, + Object state + ) { + PySimplifyBooleanCheckInspectionState inspectionState = (PySimplifyBooleanCheckInspectionState) state; + return new Visitor(holder, session, inspectionState.ignoreComparisonToZero); } - } - private static class PyBinaryExpressionVisitor extends PyInspectionVisitor { - private final boolean myIgnoreComparisonToZero; + private static class Visitor extends PyInspectionVisitor { + private final boolean myIgnoreComparisonToZero; - public PyBinaryExpressionVisitor(@Nullable ProblemsHolder holder, - @Nonnull LocalInspectionToolSession session, - boolean ignoreComparisonToZero) { - super(holder, session); - myIgnoreComparisonToZero = ignoreComparisonToZero; - } + public Visitor(@Nullable ProblemsHolder holder, @Nonnull LocalInspectionToolSession session, boolean ignoreComparisonToZero) { + super(holder, session); + myIgnoreComparisonToZero = ignoreComparisonToZero; + } - @Override - public void visitPyBinaryExpression(PyBinaryExpression node) { - super.visitPyBinaryExpression(node); - final PyElementType operator = node.getOperator(); - final PyExpression rightExpression = node.getRightExpression(); - if (rightExpression == null || rightExpression instanceof PyBinaryExpression || - node.getLeftExpression() instanceof PyBinaryExpression) { - return; - } - if (PyTokenTypes.EQUALITY_OPERATIONS.contains(operator)) { - if (operandsEqualTo(node, COMPARISON_LITERALS) || - (!myIgnoreComparisonToZero && operandsEqualTo(node, Collections.singleton("0")))) { - registerProblem(node); + @Override + public void visitPyConditionalStatementPart(PyConditionalStatementPart node) { + super.visitPyConditionalStatementPart(node); + final PyExpression condition = node.getCondition(); + if (condition != null) { + condition.accept(new PyBinaryExpressionVisitor(getHolder(), getSession(), myIgnoreComparisonToZero)); + } } - } } - private static boolean operandsEqualTo(@Nonnull PyBinaryExpression expr, @Nonnull Collection literals) { - final String leftExpressionText = expr.getLeftExpression().getText(); - final PyExpression rightExpression = expr.getRightExpression(); - final String rightExpressionText = rightExpression != null ? rightExpression.getText() : null; - for (String literal : literals) { - if (literal.equals(leftExpressionText) || literal.equals(rightExpressionText)) { - return true; + private static class PyBinaryExpressionVisitor extends PyInspectionVisitor { + private final boolean myIgnoreComparisonToZero; + + public PyBinaryExpressionVisitor( + @Nullable ProblemsHolder holder, + @Nonnull LocalInspectionToolSession session, + boolean ignoreComparisonToZero + ) { + super(holder, session); + myIgnoreComparisonToZero = ignoreComparisonToZero; } - } - return false; - } - private void registerProblem(PyBinaryExpression binaryExpression) { - registerProblem(binaryExpression, - PyBundle.message("INSP.expression.can.be.simplified"), - new SimplifyBooleanCheckQuickFix(binaryExpression)); + @Override + public void visitPyBinaryExpression(PyBinaryExpression node) { + super.visitPyBinaryExpression(node); + final PyElementType operator = node.getOperator(); + final PyExpression rightExpression = node.getRightExpression(); + if (rightExpression == null || rightExpression instanceof PyBinaryExpression || + node.getLeftExpression() instanceof PyBinaryExpression) { + return; + } + if (PyTokenTypes.EQUALITY_OPERATIONS.contains(operator)) { + if (operandsEqualTo(node, COMPARISON_LITERALS) || + (!myIgnoreComparisonToZero && operandsEqualTo(node, Collections.singleton("0")))) { + registerProblem(node); + } + } + } + + private static boolean operandsEqualTo(@Nonnull PyBinaryExpression expr, @Nonnull Collection literals) { + final String leftExpressionText = expr.getLeftExpression().getText(); + final PyExpression rightExpression = expr.getRightExpression(); + final String rightExpressionText = rightExpression != null ? rightExpression.getText() : null; + for (String literal : literals) { + if (literal.equals(leftExpressionText) || literal.equals(rightExpressionText)) { + return true; + } + } + return false; + } + + private void registerProblem(PyBinaryExpression binaryExpression) { + registerProblem( + binaryExpression, + PyLocalize.inspExpressionCanBeSimplified().get(), + new SimplifyBooleanCheckQuickFix(binaryExpression) + ); + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/quickfix/PySuppressInspectionFix.java b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/quickfix/PySuppressInspectionFix.java index d4420a3c..2a832362 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/inspections/quickfix/PySuppressInspectionFix.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/inspections/quickfix/PySuppressInspectionFix.java @@ -15,28 +15,27 @@ */ package com.jetbrains.python.impl.inspections.quickfix; +import com.jetbrains.python.psi.PyElement; import consulo.language.editor.inspection.AbstractBatchSuppressByNoInspectionCommentFix; import consulo.language.psi.PsiElement; import consulo.language.psi.util.PsiTreeUtil; -import com.jetbrains.python.psi.PyElement; +import consulo.localize.LocalizeValue; +import jakarta.annotation.Nonnull; /** * @author yole */ -public class PySuppressInspectionFix extends AbstractBatchSuppressByNoInspectionCommentFix -{ - private final Class myContainerClass; +public class PySuppressInspectionFix extends AbstractBatchSuppressByNoInspectionCommentFix { + private final Class myContainerClass; - public PySuppressInspectionFix(final String ID, final String text, final Class containerClass) - { - super(ID, false); - setText(text); - myContainerClass = containerClass; - } + public PySuppressInspectionFix(String ID, @Nonnull LocalizeValue text, Class containerClass) { + super(ID, false); + setText(text); + myContainerClass = containerClass; + } - @Override - public PsiElement getContainer(PsiElement context) - { - return PsiTreeUtil.getParentOfType(context, myContainerClass); - } + @Override + public PsiElement getContainer(PsiElement context) { + return PsiTreeUtil.getParentOfType(context, myContainerClass); + } }