mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Introduce registerForAllMatchingVersions and registerOnFirstMatchingVersion util methods in CompatibilityVisitor
This commit is contained in:
@@ -22,7 +22,6 @@ import com.intellij.codeInspection.ProblemHighlightType;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.ide.util.ElementsChooser;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.util.JDOMExternalizableStringList;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
@@ -192,24 +191,20 @@ public class PyCompatibilityInspection extends PyInspection {
|
||||
final PyClass containingClass = function.getContainingClass();
|
||||
final String originalFunctionName = function.getName();
|
||||
|
||||
final StringBuilder message = new StringBuilder(containingClass != null && !PyNames.INIT.equals(originalFunctionName)
|
||||
? "Class " + containingClass.getName() + " in python version "
|
||||
: COMMON_MESSAGE);
|
||||
|
||||
final String functionName = containingClass != null && PyNames.INIT.equals(originalFunctionName)
|
||||
? node.getCallee().getText()
|
||||
: originalFunctionName;
|
||||
|
||||
int len = 0;
|
||||
|
||||
if (containingClass != null) {
|
||||
final String className = containingClass.getName();
|
||||
|
||||
if (UnsupportedFeaturesUtil.CLASS_METHODS.containsKey(className)) {
|
||||
final Map<LanguageLevel, Set<String>> unsupportedMethods = UnsupportedFeaturesUtil.CLASS_METHODS.get(className);
|
||||
|
||||
len += appendLanguageLevels(message, myVersionsToProcess,
|
||||
level -> unsupportedMethods.getOrDefault(level, Collections.emptySet()).contains(functionName));
|
||||
registerForAllMatchingVersions(level -> unsupportedMethods.getOrDefault(level, Collections.emptySet()).contains(functionName),
|
||||
" not have method " + functionName,
|
||||
node,
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,11 +212,11 @@ public class PyCompatibilityInspection extends PyInspection {
|
||||
!"print".equals(functionName) &&
|
||||
!"exec".equals(functionName) &&
|
||||
!myUsedImports.contains(functionName)) {
|
||||
len +=
|
||||
appendLanguageLevels(message, myVersionsToProcess, level -> UnsupportedFeaturesUtil.BUILTINS.get(level).contains(functionName));
|
||||
registerForAllMatchingVersions(level -> UnsupportedFeaturesUtil.BUILTINS.get(level).contains(functionName),
|
||||
" not have method " + functionName,
|
||||
node,
|
||||
null);
|
||||
}
|
||||
|
||||
commonRegisterProblem(message, " not have method " + functionName, len, node, null, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,10 +243,10 @@ public class PyCompatibilityInspection extends PyInspection {
|
||||
final QualifiedName sourceQName = fromImportStatement.getImportSourceQName();
|
||||
|
||||
if (qName != null && sourceQName != null && qName.matches("unicode_literals") && sourceQName.matches("__future__")) {
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final int len = appendLanguageLevels(message, myVersionsToProcess, level -> level.isOlderThan(LanguageLevel.PYTHON26));
|
||||
|
||||
commonRegisterProblem(message, " not have unicode_literals in __future__ module", len, importElement, null);
|
||||
registerForAllMatchingVersions(level -> level.isOlderThan(LanguageLevel.PYTHON26),
|
||||
" not have unicode_literals in __future__ module",
|
||||
importElement,
|
||||
null);
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -260,12 +255,11 @@ public class PyCompatibilityInspection extends PyInspection {
|
||||
final QualifiedName qName = importElement.getImportedQName();
|
||||
if (qName != null && !qName.matches("builtins") && !qName.matches("__builtin__")) {
|
||||
final String moduleName = qName.toString();
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final int len = appendLanguageLevels(message, myVersionsToProcess,
|
||||
level -> UnsupportedFeaturesUtil.MODULES.get(level).contains(moduleName) &&
|
||||
!BACKPORTED_PACKAGES.contains(moduleName));
|
||||
|
||||
commonRegisterProblem(message, " not have module " + moduleName, len, importElement, null);
|
||||
registerForAllMatchingVersions(level -> UnsupportedFeaturesUtil.MODULES.get(level).contains(moduleName) && !BACKPORTED_PACKAGES.contains(moduleName),
|
||||
" not have module " + moduleName,
|
||||
importElement,
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,43 +272,43 @@ public class PyCompatibilityInspection extends PyInspection {
|
||||
final QualifiedName name = node.getImportSourceQName();
|
||||
final PyReferenceExpression source = node.getImportSource();
|
||||
if (name != null && source != null) {
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final String moduleName = name.toString();
|
||||
final int len = appendLanguageLevels(message, myVersionsToProcess,
|
||||
level -> UnsupportedFeaturesUtil.MODULES.get(level).contains(moduleName) &&
|
||||
!BACKPORTED_PACKAGES.contains(moduleName));
|
||||
|
||||
commonRegisterProblem(message, " not have module " + name, len, source, null, false);
|
||||
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
|
||||
final List<PyElement> problemElements = new ArrayList<>();
|
||||
if (node.getParent() instanceof PyClass) {
|
||||
for (final PyExpression expression : node.getArguments()) {
|
||||
if (expression instanceof PyKeywordArgument)
|
||||
problemElements.add(expression);
|
||||
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));
|
||||
}
|
||||
}
|
||||
final String errorMessage = "This syntax available only since py3";
|
||||
final boolean isPy3 = LanguageLevel.forElement(node).isPy3K();
|
||||
if (shouldBeCompatibleWithPy2() || !isPy3) {
|
||||
for (final PyElement problemElement : problemElements)
|
||||
myHolder.registerProblem(problemElement, errorMessage, isPy3? ProblemHighlightType.GENERIC_ERROR_OR_WARNING :
|
||||
ProblemHighlightType.GENERIC_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyReferenceExpression(PyReferenceExpression node) {
|
||||
super.visitPyElement(node);
|
||||
if (shouldBeCompatibleWithPy3()) {
|
||||
final TypeEvalContext context = TypeEvalContext.codeAnalysis(node.getProject(), node.getContainingFile());
|
||||
|
||||
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)) {
|
||||
@@ -324,16 +318,16 @@ public class PyCompatibilityInspection extends PyInspection {
|
||||
}
|
||||
|
||||
if (PyNames.BASESTRING.equals(nodeText)) {
|
||||
PsiElement res = node.getReference().resolve();
|
||||
final PsiElement res = node.getReference().resolve();
|
||||
if (res != null) {
|
||||
ProjectFileIndex ind = ProjectRootManager.getInstance(node.getProject()).getFileIndex();
|
||||
PsiFile file = res.getContainingFile();
|
||||
if (file != null ) {
|
||||
final PsiFile file = res.getContainingFile();
|
||||
if (file != null) {
|
||||
final VirtualFile virtualFile = file.getVirtualFile();
|
||||
if (virtualFile != null && ind.isInLibraryClasses(virtualFile)) {
|
||||
if (virtualFile != null && ProjectRootManager.getInstance(node.getProject()).getFileIndex().isInLibraryClasses(virtualFile)) {
|
||||
registerProblem(node, "basestring type is not available in py3");
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
registerProblem(node, "basestring type is not available in py3");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,16 @@ import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.inspections.quickfix.*;
|
||||
@@ -36,6 +39,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* User : catherine
|
||||
@@ -73,14 +77,10 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
public void visitPyAnnotation(PyAnnotation node) {
|
||||
final PsiElement parent = node.getParent();
|
||||
if (!(parent instanceof PyFunction || parent instanceof PyNamedParameter)) {
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
int len = 0;
|
||||
for (LanguageLevel languageLevel : myVersionsToProcess) {
|
||||
if (languageLevel.isOlderThan(LanguageLevel.PYTHON36)) {
|
||||
len = appendLanguageLevel(message, len, languageLevel);
|
||||
}
|
||||
}
|
||||
commonRegisterProblem(message, " not support variable annotations", len, node, null);
|
||||
registerForAllMatchingVersions(level -> level.isOlderThan(LanguageLevel.PYTHON36),
|
||||
" not support variable annotations",
|
||||
node,
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,27 +88,33 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
public void visitPyDictCompExpression(PyDictCompExpression node) {
|
||||
super.visitPyDictCompExpression(node);
|
||||
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final int len = appendLanguageLevels(message, myVersionsToProcess, level -> !level.supportsSetLiterals());
|
||||
commonRegisterProblem(message, " not support dictionary comprehensions", len, node, new ConvertDictCompQuickFix(), false);
|
||||
registerForAllMatchingVersions(level -> !level.supportsSetLiterals(),
|
||||
" not support dictionary comprehensions",
|
||||
node,
|
||||
new ConvertDictCompQuickFix(),
|
||||
false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPySetLiteralExpression(PySetLiteralExpression node) {
|
||||
super.visitPySetLiteralExpression(node);
|
||||
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final int len = appendLanguageLevels(message, myVersionsToProcess, level -> !level.supportsSetLiterals());
|
||||
commonRegisterProblem(message, " not support set literal expressions", len, node, new ConvertSetLiteralQuickFix(), false);
|
||||
registerForAllMatchingVersions(level -> !level.supportsSetLiterals(),
|
||||
" not support set literal expressions",
|
||||
node,
|
||||
new ConvertSetLiteralQuickFix(),
|
||||
false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPySetCompExpression(PySetCompExpression node) {
|
||||
super.visitPySetCompExpression(node);
|
||||
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final int len = appendLanguageLevels(message, myVersionsToProcess, level -> !level.supportsSetLiterals());
|
||||
commonRegisterProblem(message, " not support set comprehensions", len, node, null, false);
|
||||
registerForAllMatchingVersions(level -> !level.supportsSetLiterals(),
|
||||
" not support set comprehensions",
|
||||
node,
|
||||
null,
|
||||
false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,27 +123,19 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
|
||||
final PyExpression exceptClass = node.getExceptClass();
|
||||
if (exceptClass != null) {
|
||||
if (myVersionsToProcess.contains(LanguageLevel.PYTHON24) || myVersionsToProcess.contains(LanguageLevel.PYTHON25)) {
|
||||
PsiElement element = exceptClass.getNextSibling();
|
||||
while (element instanceof PsiWhiteSpace) {
|
||||
element = element.getNextSibling();
|
||||
}
|
||||
|
||||
if (element != null && "as".equals(element.getText())) {
|
||||
registerProblem(node, COMMON_MESSAGE + "2.4, 2.5 do not support this syntax.");
|
||||
}
|
||||
}
|
||||
|
||||
PsiElement element = exceptClass.getNextSibling();
|
||||
while (element instanceof PsiWhiteSpace) {
|
||||
element = element.getNextSibling();
|
||||
}
|
||||
|
||||
if (element != null && ",".equals(element.getText())) {
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final int len = appendLanguageLevels(message, myVersionsToProcess, LanguageLevel::isPy3K);
|
||||
if (element != null && "as".equals(element.getText())) {
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON26),
|
||||
"Python versions < 2.6 do not support this syntax.",
|
||||
node);
|
||||
}
|
||||
|
||||
commonRegisterProblem(message, " not support this syntax.", len, node, new ReplaceExceptPartQuickFix());
|
||||
if (element != null && ",".equals(element.getText())) {
|
||||
registerForAllMatchingVersions(LanguageLevel::isPy3K, " not support this syntax.", node, new ReplaceExceptPartQuickFix());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -153,16 +151,11 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
final QualifiedName qName = importElement.getImportedQName();
|
||||
|
||||
if (qName != null) {
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final int len;
|
||||
|
||||
if (qName.matches("builtins")) {
|
||||
len = appendLanguageLevels(message, myVersionsToProcess, level -> !level.isPy3K());
|
||||
commonRegisterProblem(message, " not have module builtins", len, node, new ReplaceBuiltinsQuickFix());
|
||||
registerForAllMatchingVersions(level -> !level.isPy3K(), " not have module builtins", node, new ReplaceBuiltinsQuickFix());
|
||||
}
|
||||
else if (qName.matches("__builtin__")) {
|
||||
len = appendLanguageLevels(message, myVersionsToProcess, LanguageLevel::isPy3K);
|
||||
commonRegisterProblem(message, " not have module __builtin__", len, node, new ReplaceBuiltinsQuickFix());
|
||||
registerForAllMatchingVersions(LanguageLevel::isPy3K, " not have module __builtin__", node, new ReplaceBuiltinsQuickFix());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,17 +166,15 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
super.visitPyStarExpression(node);
|
||||
|
||||
if (node.isAssignmentTarget()) {
|
||||
registerFirst(node,
|
||||
"Python versions < 3.0 do not support starred expressions as assignment targets",
|
||||
myVersionsToProcess,
|
||||
level -> level.isOlderThan(LanguageLevel.PYTHON30));
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON30),
|
||||
"Python versions < 3.0 do not support starred expressions as assignment targets",
|
||||
node);
|
||||
}
|
||||
|
||||
if (node.isUnpacking()) {
|
||||
registerFirst(node,
|
||||
"Python versions < 3.5 do not support starred expressions in tuples, lists, and sets",
|
||||
myVersionsToProcess,
|
||||
level -> level.isOlderThan(LanguageLevel.PYTHON35));
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON35),
|
||||
"Python versions < 3.5 do not support starred expressions in tuples, lists, and sets",
|
||||
node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,10 +182,9 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
public void visitPyDoubleStarExpression(PyDoubleStarExpression node) {
|
||||
super.visitPyDoubleStarExpression(node);
|
||||
|
||||
registerFirst(node,
|
||||
"Python versions < 3.5 do not support starred expressions in dicts",
|
||||
myVersionsToProcess,
|
||||
level -> level.isOlderThan(LanguageLevel.PYTHON35));
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON35),
|
||||
"Python versions < 3.5 do not support starred expressions in dicts",
|
||||
node);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -202,10 +192,7 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
super.visitPyBinaryExpression(node);
|
||||
|
||||
if (node.isOperator("<>")) {
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final int len = appendLanguageLevels(message, myVersionsToProcess, LanguageLevel::isPy3K);
|
||||
|
||||
commonRegisterProblem(message, " not support <>, use != instead.", len, node, new ReplaceNotEqOperatorQuickFix());
|
||||
registerForAllMatchingVersions(LanguageLevel::isPy3K, " not support <>, use != instead.", node, new ReplaceNotEqOperatorQuickFix());
|
||||
}
|
||||
else if (node.isOperator("@")) {
|
||||
checkMatrixMultiplicationOperator(node.getPsiOperator());
|
||||
@@ -213,10 +200,9 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
}
|
||||
|
||||
private void checkMatrixMultiplicationOperator(PsiElement node) {
|
||||
registerFirst(node,
|
||||
"Python versions < 3.5 do not support matrix multiplication operators",
|
||||
myVersionsToProcess,
|
||||
level -> level.isOlderThan(LanguageLevel.PYTHON35));
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON35),
|
||||
"Python versions < 3.5 do not support matrix multiplication operators",
|
||||
node);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -227,31 +213,28 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
|
||||
if (node.isIntegerLiteral()) {
|
||||
if (text.endsWith("l") || text.endsWith("L")) {
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final String suffix = " not support a trailing \'l\' or \'L\'.";
|
||||
final int len = appendLanguageLevels(message, myVersionsToProcess, LanguageLevel::isPy3K);
|
||||
|
||||
commonRegisterProblem(message, suffix, len, node, new RemoveTrailingLQuickFix());
|
||||
registerForAllMatchingVersions(LanguageLevel::isPy3K,
|
||||
" not support a trailing \'l\' or \'L\'.",
|
||||
node,
|
||||
new RemoveTrailingLQuickFix());
|
||||
}
|
||||
|
||||
if (text.length() > 1 && text.charAt(0) == '0') {
|
||||
final char secondChar = Character.toLowerCase(text.charAt(1));
|
||||
if (secondChar != 'o' && secondChar != 'b' && secondChar != 'x' && text.chars().anyMatch(c -> c != '0')) {
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final String suffix = " not support this syntax. It requires '0o' prefix for octal literals";
|
||||
int len = appendLanguageLevels(message, myVersionsToProcess, LanguageLevel::isPy3K);
|
||||
|
||||
commonRegisterProblem(message, suffix, len, node, new ReplaceOctalNumericLiteralQuickFix());
|
||||
registerForAllMatchingVersions(LanguageLevel::isPy3K,
|
||||
" not support this syntax. It requires '0o' prefix for octal literals",
|
||||
node,
|
||||
new ReplaceOctalNumericLiteralQuickFix());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (text.contains("_")) {
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final String suffix = " not support underscores in numeric literals";
|
||||
final int len = appendLanguageLevels(message, myVersionsToProcess, level -> level.isOlderThan(LanguageLevel.PYTHON36));
|
||||
|
||||
commonRegisterProblem(message, suffix, len, node, new PyRemoveUnderscoresInNumericLiteralsQuickFix());
|
||||
registerForAllMatchingVersions(level -> level.isOlderThan(LanguageLevel.PYTHON36),
|
||||
" not support underscores in numeric literals",
|
||||
node,
|
||||
new PyRemoveUnderscoresInNumericLiteralsQuickFix());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,14 +248,13 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
final String prefix = text.substring(0, prefixLength).toUpperCase();
|
||||
if (prefix.isEmpty()) continue;
|
||||
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final int len =
|
||||
appendLanguageLevels(message,
|
||||
myVersionsToProcess,
|
||||
level -> !AVAILABLE_PREFIXES.getOrDefault(level, DEFAULT_PREFIXES).contains(prefix));
|
||||
|
||||
final TextRange range = TextRange.create(stringNode.getStartOffset(), stringNode.getStartOffset() + prefixLength);
|
||||
commonRegisterProblem(message, " not support a '" + prefix + "' prefix", len, node, range, new RemovePrefixQuickFix(prefix));
|
||||
registerForAllMatchingVersions(level -> !AVAILABLE_PREFIXES.getOrDefault(level, DEFAULT_PREFIXES).contains(prefix),
|
||||
" not support a '" + prefix + "' prefix",
|
||||
node,
|
||||
range,
|
||||
new RemovePrefixQuickFix(prefix),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,14 +262,11 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
public void visitPyListCompExpression(final PyListCompExpression node) {
|
||||
super.visitPyListCompExpression(node);
|
||||
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final int len =
|
||||
appendLanguageLevels(message, myVersionsToProcess, level -> UnsupportedFeaturesUtil.visitPyListCompExpression(node, level));
|
||||
|
||||
for (ComprhForComponent forComponent : node.getForComponents()) {
|
||||
commonRegisterProblem(message, " not support this syntax in list comprehensions.", len, forComponent.getIteratedList(),
|
||||
new ReplaceListComprehensionsQuickFix());
|
||||
}
|
||||
final List<PyExpression> nodes = node.getForComponents().stream().map(ComprhForComponent::getIteratedList).collect(Collectors.toList());
|
||||
registerForAllMatchingVersions(level -> UnsupportedFeaturesUtil.visitPyListCompExpression(node, level),
|
||||
" not support this syntax in list comprehensions.",
|
||||
nodes,
|
||||
new ReplaceListComprehensionsQuickFix());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -295,58 +274,50 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
super.visitPyRaiseStatement(node);
|
||||
|
||||
// empty raise
|
||||
StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
int len = appendLanguageLevels(message, myVersionsToProcess, level -> UnsupportedFeaturesUtil.raiseHasNoArgs(node, level));
|
||||
commonRegisterProblem(message, " not support this syntax. Raise with no arguments can only be used in an except block",
|
||||
len, node, null, false);
|
||||
registerForAllMatchingVersions(level -> UnsupportedFeaturesUtil.raiseHasNoArgs(node, level),
|
||||
" not support this syntax. Raise with no arguments can only be used in an except block",
|
||||
node,
|
||||
null,
|
||||
false);
|
||||
|
||||
// raise 1, 2, 3
|
||||
message = new StringBuilder(COMMON_MESSAGE);
|
||||
len = appendLanguageLevels(message, myVersionsToProcess, level -> UnsupportedFeaturesUtil.raiseHasMoreThenOneArg(node, level));
|
||||
commonRegisterProblem(message, " not support this syntax.", len, node, new ReplaceRaiseStatementQuickFix());
|
||||
registerForAllMatchingVersions(level -> UnsupportedFeaturesUtil.raiseHasMoreThenOneArg(node, level),
|
||||
" not support this syntax.",
|
||||
node,
|
||||
new ReplaceRaiseStatementQuickFix());
|
||||
|
||||
// raise exception from cause
|
||||
message = new StringBuilder(COMMON_MESSAGE);
|
||||
len = appendLanguageLevels(message, myVersionsToProcess, level -> UnsupportedFeaturesUtil.raiseHasFromKeyword(node, level));
|
||||
commonRegisterProblem(message, " not support this syntax.", len, node, new ReplaceRaiseStatementQuickFix());
|
||||
registerForAllMatchingVersions(level -> UnsupportedFeaturesUtil.raiseHasFromKeyword(node, level),
|
||||
" not support this syntax.",
|
||||
node,
|
||||
new ReplaceRaiseStatementQuickFix());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyReprExpression(PyReprExpression node) {
|
||||
super.visitPyReprExpression(node);
|
||||
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final int len = appendLanguageLevels(message, myVersionsToProcess, LanguageLevel::isPy3K);
|
||||
commonRegisterProblem(message, " not support backquotes, use repr() instead", len, node, new ReplaceBackquoteExpressionQuickFix());
|
||||
registerForAllMatchingVersions(LanguageLevel::isPy3K,
|
||||
" not support backquotes, use repr() instead",
|
||||
node,
|
||||
new ReplaceBackquoteExpressionQuickFix());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void visitPyWithStatement(PyWithStatement node) {
|
||||
super.visitPyWithStatement(node);
|
||||
Set<PyWithItem> problemItems = new HashSet<>();
|
||||
StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
|
||||
LanguageLevel languageLevel = myVersionsToProcess.get(i);
|
||||
if (languageLevel == LanguageLevel.PYTHON24) {
|
||||
registerProblem(node, "Python version 2.4 doesn't support this syntax.");
|
||||
}
|
||||
else if (!languageLevel.supportsSetLiterals()) {
|
||||
final PyWithItem[] items = node.getWithItems();
|
||||
if (items.length > 1) {
|
||||
for (int j = 1; j < items.length; j++) {
|
||||
if (!problemItems.isEmpty())
|
||||
message.append(", ");
|
||||
message.append(languageLevel.toString());
|
||||
problemItems.add(items [j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
message.append(" do not support multiple context managers");
|
||||
for (PyWithItem item : problemItems) {
|
||||
registerProblem(item, message.toString());
|
||||
|
||||
registerOnFirstMatchingVersion(LanguageLevel.PYTHON24::equals, "Python version 2.4 doesn't support this syntax.", node);
|
||||
|
||||
final PyWithItem[] items = node.getWithItems();
|
||||
if (items.length > 1) {
|
||||
registerForAllMatchingVersions(level -> !level.supportsSetLiterals() && !level.equals(LanguageLevel.PYTHON24),
|
||||
" not support multiple context managers",
|
||||
Arrays.asList(items).subList(1, items.length),
|
||||
null);
|
||||
}
|
||||
|
||||
checkAsyncKeyword(node);
|
||||
}
|
||||
|
||||
@@ -362,25 +333,24 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
|
||||
final PyArgumentList list = node.getSuperClassExpressionList();
|
||||
if (list != null && list.getArguments().length == 0) {
|
||||
registerFirst(list, "Python version 2.4 does not support this syntax.", myVersionsToProcess, LanguageLevel.PYTHON24::equals);
|
||||
registerOnFirstMatchingVersion(LanguageLevel.PYTHON24::equals, "Python version 2.4 does not support this syntax.", list);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyPrintStatement(PyPrintStatement node) {
|
||||
super.visitPyPrintStatement(node);
|
||||
if (shouldBeCompatibleWithPy3()) {
|
||||
boolean hasProblem = false;
|
||||
PsiElement[] arguments = node.getChildren();
|
||||
for (PsiElement element : arguments) {
|
||||
if (!((element instanceof PyParenthesizedExpression) || (element instanceof PyTupleExpression))) {
|
||||
hasProblem = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasProblem || arguments.length == 0)
|
||||
registerProblem(node, "Python version >= 3.0 do not support this syntax. The print statement has been replaced with a print() function",
|
||||
new CompatibilityPrintCallQuickFix());
|
||||
|
||||
final PsiElement[] arguments = node.getChildren();
|
||||
final Predicate<PsiElement> nonParenthesesPredicate =
|
||||
element -> !(element instanceof PyParenthesizedExpression || element instanceof PyTupleExpression);
|
||||
|
||||
if (arguments.length == 0 || Arrays.stream(arguments).anyMatch(nonParenthesesPredicate)) {
|
||||
registerOnFirstMatchingVersion(LanguageLevel::isPy3K,
|
||||
"Python version >= 3.0 do not support this syntax. " +
|
||||
"The print statement has been replaced with a print() function",
|
||||
node,
|
||||
new CompatibilityPrintCallQuickFix());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,11 +362,11 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
if (importSource != null) {
|
||||
final PsiElement prev = importSource.getPrevSibling();
|
||||
if (prev != null && prev.getNode().getElementType() == PyTokenTypes.DOT) { // PY-2793
|
||||
registerFirst(node, "Python version 2.4 doesn't support this syntax.", myVersionsToProcess, LanguageLevel.PYTHON24::equals);
|
||||
registerOnFirstMatchingVersion(LanguageLevel.PYTHON24::equals, "Python version 2.4 doesn't support this syntax.", node);
|
||||
}
|
||||
}
|
||||
else {
|
||||
registerFirst(node, "Python version 2.4 doesn't support this syntax.", myVersionsToProcess, LanguageLevel.PYTHON24::equals);
|
||||
registerOnFirstMatchingVersion(LanguageLevel.PYTHON24::equals, "Python version 2.4 doesn't support this syntax.", node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,10 +396,7 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
public void visitPyConditionalExpression(PyConditionalExpression node) { //PY-4293
|
||||
super.visitPyConditionalExpression(node);
|
||||
|
||||
registerFirst(node,
|
||||
"Python version 2.4 doesn't support this syntax.",
|
||||
myVersionsToProcess,
|
||||
LanguageLevel.PYTHON24::equals);
|
||||
registerOnFirstMatchingVersion(LanguageLevel.PYTHON24::equals, "Python version 2.4 doesn't support this syntax.", node);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -439,11 +406,10 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
final PyExceptPart[] excepts = node.getExceptParts();
|
||||
final PyFinallyPart finallyPart = node.getFinallyPart();
|
||||
if (excepts.length != 0 && finallyPart != null) {
|
||||
registerFirst(node,
|
||||
"Python version 2.4 doesn't support this syntax. You could use a finally block to ensure " +
|
||||
"that code is always executed, or one or more except blocks to catch specific exceptions.",
|
||||
myVersionsToProcess,
|
||||
LanguageLevel.PYTHON24::equals);
|
||||
registerOnFirstMatchingVersion(LanguageLevel.PYTHON24::equals,
|
||||
"Python version 2.4 doesn't support this syntax. You could use a finally block to ensure " +
|
||||
"that code is always executed, or one or more except blocks to catch specific exceptions.",
|
||||
node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -453,10 +419,10 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
|
||||
final PsiElement firstChild = node.getFirstChild();
|
||||
if (firstChild != null && PyNames.SUPER.equals(firstChild.getText()) && ArrayUtil.isEmpty(node.getArguments())) {
|
||||
final StringBuilder message = new StringBuilder(COMMON_MESSAGE);
|
||||
final int len = appendLanguageLevels(message, myVersionsToProcess, level -> !level.isPy3K());
|
||||
|
||||
commonRegisterProblem(message, " not support this syntax. super() should have arguments in Python 2", len, node, null);
|
||||
registerForAllMatchingVersions(level -> !level.isPy3K(),
|
||||
" not support this syntax. super() should have arguments in Python 2",
|
||||
node,
|
||||
null);
|
||||
}
|
||||
|
||||
highlightIncorrectArguments(node);
|
||||
@@ -473,10 +439,9 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
super.visitPyPrefixExpression(node);
|
||||
|
||||
if (node.getOperator() == PyTokenTypes.AWAIT_KEYWORD) {
|
||||
registerFirst(node,
|
||||
"Python versions < 3.5 do not support this syntax",
|
||||
myVersionsToProcess,
|
||||
level -> level.isOlderThan(LanguageLevel.PYTHON35));
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON35),
|
||||
"Python versions < 3.5 do not support this syntax",
|
||||
node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,38 +453,29 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
return;
|
||||
}
|
||||
|
||||
registerFirst(node,
|
||||
"Python versions < 3.3 do not support this syntax. Delegating to a subgenerator is available since " +
|
||||
"Python 3.3; use explicit iteration over subgenerator instead.",
|
||||
myVersionsToProcess,
|
||||
level -> level.isOlderThan(LanguageLevel.PYTHON33));
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON33),
|
||||
"Python versions < 3.3 do not support this syntax. Delegating to a subgenerator is available since " +
|
||||
"Python 3.3; use explicit iteration over subgenerator instead.",
|
||||
node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyReturnStatement(PyReturnStatement node) {
|
||||
boolean allowed = true;
|
||||
for (LanguageLevel level : myVersionsToProcess) {
|
||||
if (level.isOlderThan(LanguageLevel.PYTHON33)) {
|
||||
allowed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allowed) {
|
||||
return;
|
||||
}
|
||||
final PyFunction function = PsiTreeUtil.getParentOfType(node, PyFunction.class, false, PyClass.class);
|
||||
if (function != null && node.getExpression() != null) {
|
||||
final YieldVisitor visitor = new YieldVisitor();
|
||||
function.acceptChildren(visitor);
|
||||
if (visitor.haveYield()) {
|
||||
registerProblem(node, "Python versions < 3.3 do not allow 'return' with argument inside generator.");
|
||||
if (myVersionsToProcess.stream().anyMatch(level -> level.isOlderThan(LanguageLevel.PYTHON33))) {
|
||||
final PyFunction function = PsiTreeUtil.getParentOfType(node, PyFunction.class, false, PyClass.class);
|
||||
if (function != null && node.getExpression() != null) {
|
||||
final YieldVisitor visitor = new YieldVisitor();
|
||||
function.acceptChildren(visitor);
|
||||
if (visitor.haveYield()) {
|
||||
registerProblem(node, "Python versions < 3.3 do not allow 'return' with argument inside generator.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyNoneLiteralExpression(PyNoneLiteralExpression node) {
|
||||
if (shouldBeCompatibleWithPy2() && node.isEllipsis()) {
|
||||
if (node.isEllipsis()) {
|
||||
final PySubscriptionExpression subscription = PsiTreeUtil.getParentOfType(node, PySubscriptionExpression.class);
|
||||
if (subscription != null && PsiTreeUtil.isAncestor(subscription.getIndexExpression(), node, false)) {
|
||||
return;
|
||||
@@ -528,7 +484,9 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
if (sliceItem != null) {
|
||||
return;
|
||||
}
|
||||
registerProblem(node, "Python versions < 3.0 do not support '...' outside of sequence slicings.");
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON30),
|
||||
"Python versions < 3.0 do not support '...' outside of sequence slicings.",
|
||||
node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,12 +505,11 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
private void checkAsyncKeyword(@NotNull PsiElement node) {
|
||||
final ASTNode asyncNode = node.getNode().findChildByType(PyTokenTypes.ASYNC_KEYWORD);
|
||||
if (asyncNode != null) {
|
||||
for (LanguageLevel level : myVersionsToProcess) {
|
||||
if (level.isOlderThan(LanguageLevel.PYTHON35)) {
|
||||
registerProblem(node, asyncNode.getTextRange(), "Python versions < 3.5 do not support this syntax", null, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON35),
|
||||
"Python versions < 3.5 do not support this syntax",
|
||||
node,
|
||||
asyncNode.getTextRange(),
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -604,111 +561,85 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
myVersionsToProcess = versionsToProcess;
|
||||
}
|
||||
|
||||
protected void commonRegisterProblem(@NotNull StringBuilder initMessage,
|
||||
@NotNull String suffix,
|
||||
int len,
|
||||
@NotNull PyElement node,
|
||||
@Nullable LocalQuickFix localQuickFix) {
|
||||
commonRegisterProblem(initMessage, suffix, len, node, node.getTextRange(), localQuickFix, true);
|
||||
}
|
||||
protected void registerForAllMatchingVersions(@NotNull Predicate<LanguageLevel> levelPredicate,
|
||||
@NotNull String suffix,
|
||||
@NotNull Iterable<Pair<? extends PsiElement, TextRange>> nodesWithRanges,
|
||||
@Nullable LocalQuickFix localQuickFix,
|
||||
boolean asError) {
|
||||
final List<String> levels = myVersionsToProcess
|
||||
.stream()
|
||||
.filter(levelPredicate)
|
||||
.map(LanguageLevel::toString)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
protected void commonRegisterProblem(@NotNull StringBuilder initMessage,
|
||||
@NotNull String suffix,
|
||||
int len,
|
||||
@NotNull PyElement node,
|
||||
@NotNull TextRange range,
|
||||
@Nullable LocalQuickFix localQuickFix) {
|
||||
commonRegisterProblem(initMessage, suffix, len, node, range, localQuickFix, true);
|
||||
}
|
||||
|
||||
protected void commonRegisterProblem(@NotNull StringBuilder initMessage,
|
||||
@NotNull String suffix,
|
||||
int len,
|
||||
@NotNull PyElement node,
|
||||
@NotNull TextRange range,
|
||||
@Nullable LocalQuickFix localQuickFix,
|
||||
boolean asError) {
|
||||
initMessage.append(" do");
|
||||
if (len == 1) {
|
||||
initMessage.append("es");
|
||||
}
|
||||
initMessage.append(suffix);
|
||||
if (len != 0) {
|
||||
registerProblem(node, range, initMessage.toString(), localQuickFix, asError);
|
||||
}
|
||||
}
|
||||
|
||||
protected void commonRegisterProblem(@NotNull StringBuilder initMessage,
|
||||
@NotNull String suffix,
|
||||
int len,
|
||||
@NotNull PyElement node,
|
||||
@Nullable LocalQuickFix localQuickFix,
|
||||
boolean asError) {
|
||||
commonRegisterProblem(initMessage, suffix, len, node, node.getTextRange(), localQuickFix, asError);
|
||||
}
|
||||
|
||||
protected static int appendLanguageLevel(@NotNull StringBuilder message, int len, @NotNull LanguageLevel languageLevel) {
|
||||
if (len != 0) {
|
||||
message.append(", ");
|
||||
}
|
||||
message.append(languageLevel.toString());
|
||||
return ++len;
|
||||
}
|
||||
|
||||
protected static int appendLanguageLevels(@NotNull StringBuilder message,
|
||||
@NotNull Collection<LanguageLevel> levels,
|
||||
@NotNull Predicate<LanguageLevel> levelPredicate) {
|
||||
int len = 0;
|
||||
|
||||
for (LanguageLevel languageLevel : levels) {
|
||||
if (levelPredicate.test(languageLevel)) {
|
||||
len = appendLanguageLevel(message, len, languageLevel);
|
||||
if (!levels.isEmpty()) {
|
||||
final String result = COMMON_MESSAGE + StringUtil.join(levels, ", ") + (levels.size() == 1 ? " does" : " do") + suffix;
|
||||
for (Pair<? extends PsiElement, TextRange> nodeWithRange : nodesWithRanges) {
|
||||
registerProblem(nodeWithRange.first, nodeWithRange.second, result, localQuickFix, asError);
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
private void registerFirst(@Nullable PsiElement node,
|
||||
@NotNull String message,
|
||||
@NotNull Collection<LanguageLevel> levels,
|
||||
@NotNull Predicate<LanguageLevel> levelPredicate) {
|
||||
registerFirst(node, message, null, levels, levelPredicate);
|
||||
protected void registerForAllMatchingVersions(@NotNull Predicate<LanguageLevel> levelPredicate,
|
||||
@NotNull String suffix,
|
||||
@NotNull Iterable<? extends PsiElement> nodes,
|
||||
@Nullable LocalQuickFix localQuickFix) {
|
||||
final List<Pair<? extends PsiElement, TextRange>> nodesWithRanges =
|
||||
ContainerUtil.map(nodes, node -> Pair.createNonNull(node, node.getTextRange()));
|
||||
registerForAllMatchingVersions(levelPredicate, suffix, nodesWithRanges, localQuickFix, true);
|
||||
}
|
||||
|
||||
private void registerFirst(@Nullable PsiElement node,
|
||||
@NotNull String message,
|
||||
@Nullable LocalQuickFix localQuickFix,
|
||||
@NotNull Collection<LanguageLevel> levels,
|
||||
@NotNull Predicate<LanguageLevel> levelPredicate) {
|
||||
if (levels.stream().anyMatch(levelPredicate)) {
|
||||
registerProblem(node, message, localQuickFix);
|
||||
protected void registerForAllMatchingVersions(@NotNull Predicate<LanguageLevel> levelPredicate,
|
||||
@NotNull String suffix,
|
||||
@NotNull PsiElement node,
|
||||
@NotNull TextRange range,
|
||||
@Nullable LocalQuickFix localQuickFix,
|
||||
boolean asError) {
|
||||
final List<Pair<? extends PsiElement, TextRange>> nodesWithRanges = Collections.singletonList(Pair.createNonNull(node, range));
|
||||
registerForAllMatchingVersions(levelPredicate, suffix, nodesWithRanges, localQuickFix, asError);
|
||||
}
|
||||
|
||||
protected void registerForAllMatchingVersions(@NotNull Predicate<LanguageLevel> levelPredicate,
|
||||
@NotNull String suffix,
|
||||
@NotNull PsiElement node,
|
||||
@Nullable LocalQuickFix localQuickFix,
|
||||
boolean asError) {
|
||||
registerForAllMatchingVersions(levelPredicate, suffix, node, node.getTextRange(), localQuickFix, asError);
|
||||
}
|
||||
|
||||
protected void registerForAllMatchingVersions(@NotNull Predicate<LanguageLevel> levelPredicate,
|
||||
@NotNull String suffix,
|
||||
@NotNull PsiElement node,
|
||||
@Nullable LocalQuickFix localQuickFix) {
|
||||
registerForAllMatchingVersions(levelPredicate, suffix, node, node.getTextRange(), localQuickFix, true);
|
||||
}
|
||||
|
||||
protected void registerOnFirstMatchingVersion(@NotNull Predicate<LanguageLevel> levelPredicate,
|
||||
@NotNull String message,
|
||||
@NotNull PsiElement node,
|
||||
@NotNull TextRange range,
|
||||
@Nullable LocalQuickFix localQuickFix) {
|
||||
if (myVersionsToProcess.stream().anyMatch(levelPredicate)) {
|
||||
registerProblem(node, range, message, localQuickFix, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected void registerOnFirstMatchingVersion(@NotNull Predicate<LanguageLevel> levelPredicate,
|
||||
@NotNull String message,
|
||||
@NotNull PsiElement node,
|
||||
@Nullable LocalQuickFix localQuickFix) {
|
||||
registerOnFirstMatchingVersion(levelPredicate, message, node, node.getTextRange(), localQuickFix);
|
||||
}
|
||||
|
||||
protected void registerOnFirstMatchingVersion(@NotNull Predicate<LanguageLevel> levelPredicate,
|
||||
@NotNull String message,
|
||||
@NotNull PsiElement node) {
|
||||
registerOnFirstMatchingVersion(levelPredicate, message, node, node.getTextRange(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyNonlocalStatement(final PyNonlocalStatement node) {
|
||||
if (shouldBeCompatibleWithPy2()) {
|
||||
registerProblem(node, "nonlocal keyword available only since py3", null, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean shouldBeCompatibleWithPy2() {
|
||||
for (LanguageLevel level : myVersionsToProcess) {
|
||||
if (level.isOlderThan(LanguageLevel.PYTHON30)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean shouldBeCompatibleWithPy3() {
|
||||
for (LanguageLevel level : myVersionsToProcess) {
|
||||
if (level.isPy3K()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON30), "nonlocal keyword available only since py3", node);
|
||||
}
|
||||
|
||||
private void highlightIncorrectArguments(@NotNull PyCallExpression callExpression) {
|
||||
@@ -726,18 +657,16 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
registerProblem(argument, "Keyword argument repeated", new PyRemoveArgumentQuickFix());
|
||||
}
|
||||
else if (seenPositionalContainer) {
|
||||
registerFirst(argument,
|
||||
"Python versions < 2.6 do not allow keyword arguments after *expression",
|
||||
new PyRemoveArgumentQuickFix(),
|
||||
myVersionsToProcess,
|
||||
level -> level.isOlderThan(LanguageLevel.PYTHON26));
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON26),
|
||||
"Python versions < 2.6 do not allow keyword arguments after *expression",
|
||||
argument,
|
||||
new PyRemoveArgumentQuickFix());
|
||||
}
|
||||
else if (seenKeywordContainer) {
|
||||
registerFirst(argument,
|
||||
"Python versions < 3.5 do not allow keyword arguments after **expression",
|
||||
new PyRemoveArgumentQuickFix(),
|
||||
myVersionsToProcess,
|
||||
level -> level.isOlderThan(LanguageLevel.PYTHON35));
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON35),
|
||||
"Python versions < 3.5 do not allow keyword arguments after **expression",
|
||||
argument,
|
||||
new PyRemoveArgumentQuickFix());
|
||||
}
|
||||
|
||||
seenKeywordArgument = true;
|
||||
@@ -747,21 +676,19 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
final PyStarArgument starArgument = (PyStarArgument)argument;
|
||||
if (starArgument.isKeyword()) {
|
||||
if (seenKeywordContainer) {
|
||||
registerFirst(argument,
|
||||
"Python versions < 3.5 do not allow duplicate **expressions",
|
||||
new PyRemoveArgumentQuickFix(),
|
||||
myVersionsToProcess,
|
||||
level -> level.isOlderThan(LanguageLevel.PYTHON35));
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON35),
|
||||
"Python versions < 3.5 do not allow duplicate **expressions",
|
||||
argument,
|
||||
new PyRemoveArgumentQuickFix());
|
||||
}
|
||||
seenKeywordContainer = true;
|
||||
}
|
||||
else {
|
||||
if (seenPositionalContainer) {
|
||||
registerFirst(argument,
|
||||
"Python versions < 3.5 do not allow duplicate *expressions",
|
||||
new PyRemoveArgumentQuickFix(),
|
||||
myVersionsToProcess,
|
||||
level -> level.isOlderThan(LanguageLevel.PYTHON35));
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON35),
|
||||
"Python versions < 3.5 do not allow duplicate *expressions",
|
||||
argument,
|
||||
new PyRemoveArgumentQuickFix());
|
||||
}
|
||||
seenPositionalContainer = true;
|
||||
}
|
||||
@@ -771,11 +698,10 @@ public abstract class CompatibilityVisitor extends PyAnnotator {
|
||||
registerProblem(argument, "Positional argument after keyword argument", new PyRemoveArgumentQuickFix());
|
||||
}
|
||||
else if (seenPositionalContainer) {
|
||||
registerFirst(argument,
|
||||
"Python versions < 3.5 do not allow positional arguments after *expression",
|
||||
new PyRemoveArgumentQuickFix(),
|
||||
myVersionsToProcess,
|
||||
level -> level.isOlderThan(LanguageLevel.PYTHON35));
|
||||
registerOnFirstMatchingVersion(level -> level.isOlderThan(LanguageLevel.PYTHON35),
|
||||
"Python versions < 3.5 do not allow positional arguments after *expression",
|
||||
argument,
|
||||
new PyRemoveArgumentQuickFix());
|
||||
}
|
||||
else if (seenKeywordContainer) {
|
||||
registerProblem(argument, "Positional argument after **expression", new PyRemoveArgumentQuickFix());
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
try:
|
||||
do_smth()
|
||||
<warning descr="Python version 2.4, 2.5 do not support this syntax.">except ImportError as e:
|
||||
<warning descr="Python versions < 2.6 do not support this syntax.">except ImportError as e:
|
||||
do()</warning>
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user