UnsupportedFeatures and PyCompatiblityInspection refactored.

This commit is contained in:
Ekaterina Tuzova
2011-02-15 13:22:41 +03:00
parent f79f77c87a
commit 4409191c74
32 changed files with 811 additions and 934 deletions
@@ -0,0 +1,39 @@
package com.jetbrains.python.actions;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.psi.PyElementGenerator;
import com.jetbrains.python.psi.PyNumericLiteralExpression;
import org.jetbrains.annotations.NotNull;
/**
* Created by IntelliJ IDEA.
* Author: Alexey.Ivanov
* Date: 06.03.2010
* Time: 16:50:53
*/
public class RemoveTrailingLQuickFix implements LocalQuickFix {
@NotNull
@Override
public String getName() {
return PyBundle.message("INTN.remove.trailing.l");
}
@NotNull
public String getFamilyName() {
return PyBundle.message("INTN.Family.migration.to.python3");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement numericLiteralExpression = descriptor.getPsiElement();
if (numericLiteralExpression instanceof PyNumericLiteralExpression) {
PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project);
String text = numericLiteralExpression.getText();
numericLiteralExpression.replace(elementGenerator.createExpressionFromText(text.substring(0, text.length() - 1)));
}
}
}
@@ -34,14 +34,11 @@ public class ReplaceBuiltinsQuickFix implements LocalQuickFix {
for (PyImportElement importElement : ((PyImportStatement)importStatement).getImportElements()) {
PyReferenceExpression importReference = importElement.getImportReference();
if (importReference != null) {
if (LanguageLevel.forFile(importStatement.getContainingFile().getVirtualFile()).isPy3K()) {
if ("__builtin__".equals(importReference.getName())) {
importReference.replace(elementGenerator.createFromText(LanguageLevel.getDefault(), PyReferenceExpression.class, "builtins"));
}
} else {
if ("builtins".equals(importReference.getName())) {
importReference.replace(elementGenerator.createFromText(LanguageLevel.getDefault(), PyReferenceExpression.class, "__builtin__"));
}
if ("__builtin__".equals(importReference.getName())) {
importReference.replace(elementGenerator.createFromText(LanguageLevel.getDefault(), PyReferenceExpression.class, "builtins"));
}
if ("builtins".equals(importReference.getName())) {
importReference.replace(elementGenerator.createFromText(LanguageLevel.getDefault(), PyReferenceExpression.class, "__builtin__"));
}
}
}
@@ -1,50 +0,0 @@
package com.jetbrains.python.codeInsight.intentions;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PythonLanguage;
import com.jetbrains.python.psi.PyElementGenerator;
import com.jetbrains.python.psi.PyNumericLiteralExpression;
import org.jetbrains.annotations.NotNull;
/**
* Created by IntelliJ IDEA.
* Author: Alexey.Ivanov
* Date: 06.03.2010
* Time: 16:50:53
*/
public class RemoveTrailingLIntention implements IntentionAction {
@NotNull
public String getText() {
return PyBundle.message("INTN.remove.trailing.l");
}
@NotNull
public String getFamilyName() {
return PyBundle.message("INTN.Family.migration.to.python3");
}
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return true;
}
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
PyNumericLiteralExpression numericLiteralExpression =
PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyNumericLiteralExpression.class);
if (numericLiteralExpression == null) {
return;
}
PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project);
String text = numericLiteralExpression.getText();
numericLiteralExpression.replace(elementGenerator.createExpressionFromText(text.substring(0, text.length() - 1)));
}
public boolean startInWriteAction() {
return true;
}
}
@@ -1,20 +1,17 @@
package com.jetbrains.python.inspections;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.psi.*;
import com.intellij.util.containers.HashSet;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.actions.*;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.validation.CompatibilityVisitor;
import com.jetbrains.python.validation.UnsupportedFeaturesUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
@@ -22,8 +19,6 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Stack;
/**
* User: catherine
@@ -111,521 +106,23 @@ public class PyCompatibilityInspection extends PyInspection {
return new Visitor(holder, myVersionsToProcess);
}
private static class Visitor extends PyInspectionVisitor {
List<LanguageLevel> myVersionsToProcess;
private String myCommonMessage = "Python versions ";
public Visitor(final ProblemsHolder holder, List<LanguageLevel> versionsToProcess) {
super(holder);
myVersionsToProcess = versionsToProcess;
private static class Visitor extends CompatibilityVisitor {
private final ProblemsHolder myHolder;
public Visitor(ProblemsHolder holder, List<LanguageLevel> versionsToProcess) {
super(versionsToProcess);
myHolder = holder;
}
@Override
public void visitPyDictCompExpression(PyDictCompExpression node) {
super.visitPyDictCompExpression(node);
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (!languageLevel.supportsSetLiterals()) {
if (hasProblem)
message.append(", ");
message.append(languageLevel.toString());
hasProblem = true;
}
}
message.append(" do not support dictionary comprehensions");
if (hasProblem)
registerProblem(node, message.toString(), new ConvertDictCompQuickFix());
}
@Override
public void visitPySetLiteralExpression(PySetLiteralExpression node) {
super.visitPySetLiteralExpression(node);
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (!languageLevel.supportsSetLiterals()) {
if (hasProblem)
message.append(", ");
message.append(languageLevel.toString());
hasProblem = true;
}
}
message.append(" do not support set literal expressions");
if (hasProblem)
registerProblem(node, message.toString(), new ConvertSetLiteralQuickFix());
}
@Override
public void visitPySetCompExpression(PySetCompExpression node) {
super.visitPySetCompExpression(node);
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (!languageLevel.supportsSetLiterals()) {
if (hasProblem)
message.append(", ");
message.append(languageLevel.toString());
hasProblem = true;
}
}
message.append(" do not support set comprehensions");
if (hasProblem)
registerProblem(node, message.toString());
}
@Override
public void visitPyExceptBlock(PyExceptPart node) {
super.visitPyExceptBlock(node);
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, myCommonMessage + "2.4 2.5 " + " do not support this syntax.");
}
}
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (languageLevel.isPy3K()) {
PsiElement element = exceptClass.getNextSibling();
while (element instanceof PsiWhiteSpace) {
element = element.getNextSibling();
}
if (element != null && ",".equals(element.getText())) {
if (hasProblem)
message.append(", ");
message.append(languageLevel.toString());
hasProblem = true;
}
}
}
message.append(" do not this syntax.");
if (hasProblem)
registerProblem(node, message.toString(), new ReplaceExceptPartQuickFix());
}
}
@Override
public void visitPyImportStatement(PyImportStatement node) {
super.visitPyImportStatement(node);
PyImportElement[] importElements = node.getImportElements();
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
for (PyImportElement importElement : importElements) {
final PyQualifiedName qName = importElement.getImportedQName();
if (qName != null) {
if (!languageLevel.isPy3K()) {
if (qName.matches("builtins")) {
if (hasProblem)
message.append(", ");
message.append(languageLevel.toString());
message.append(" do not have module builtins.");
hasProblem = true;
}
}
else {
if (qName.matches("__builtin__")) {
if (hasProblem)
message.append(", ");
message.append(languageLevel.toString());
message.append(" do not have module __builtin__.");
hasProblem = true;
}
}
}
}
}
if (hasProblem)
registerProblem(node, message.toString(), new ReplaceBuiltinsQuickFix());
}
@Override
public void visitPyStarExpression(PyStarExpression node) {
super.visitPyStarExpression(node);
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (!languageLevel.isPy3K()) {
if (hasProblem)
message.append(", ");
message.append(languageLevel.toString());
hasProblem = true;
}
}
message.append(" do not support this syntax. Starred expressions are not allowed as assignment targets in Python 2");
if (hasProblem)
registerProblem(node, message.toString());
}
@Override
public void visitPyBinaryExpression(PyBinaryExpression node) {
super.visitPyBinaryExpression(node);
boolean hasProblem = false;
if (node.isOperator("<>")) {
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel level = myVersionsToProcess.get(i);
if (level.isPy3K()) {
if (hasProblem)
message.append(", ");
message.append(level.toString());
hasProblem = true;
}
}
message.append(" do not support <>, use != instead.");
if (hasProblem)
registerProblem(node, message.toString(), new ReplaceNotEqOperatorQuickFix());
}
}
@Override
public void visitPyNumericLiteralExpression(final PyNumericLiteralExpression node) {
super.visitPyNumericLiteralExpression(node);
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (languageLevel.isPy3K()) {
if (!node.isIntegerLiteral()) {
continue;
}
final String text = node.getText();
if (text.endsWith("l") || text.endsWith("L")) {
message.append(languageLevel.toString()).append(" ");
hasProblem = true;
}
if (text.length() > 1 && text.charAt(0) == '0') {
final char c = Character.toLowerCase(text.charAt(1));
if (c != 'o' && c != 'b' && c != 'x') {
boolean isNull = true;
for (char a : text.toCharArray()) {
if ( a != '0') {
isNull = false;
break;
}
}
if (!isNull) {
if (hasProblem)
message.append(", ");
message.append(languageLevel.toString());
hasProblem = true;
}
}
}
}
}
message.append(" do not support a trailing \'l\' or \'L\'.");
if (hasProblem)
registerProblem(node, message.toString(), new ReplaceOctalNumericLiteralQuickFix());
}
@Override
public void visitPyStringLiteralExpression(final PyStringLiteralExpression node) {
super.visitPyStringLiteralExpression(node);
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (languageLevel.isPy3K()) {
final String text = node.getText();
if (text.startsWith("u") || text.startsWith("U")) {
if (hasProblem)
message.append(", ");
message.append(languageLevel.toString());
hasProblem = true;
}
}
}
message.append(" do not a leading \'u\' or \'U\'.");
if (hasProblem)
registerProblem(node, message.toString(), new RemoveLeadingUQuickFix());
}
public void visitPyListCompExpression(final PyListCompExpression node) {
super.visitPyListCompExpression(node);
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
boolean tmp = UnsupportedFeaturesUtil.visitPyListCompExpression(node, languageLevel);
if (tmp) {
if (hasProblem)
message.append(", ");
message.append(languageLevel);
hasProblem = true;
}
}
message.append(" do not support this syntax in list comprehensions.");
for (ComprhForComponent forComponent : node.getForComponents()) {
final PyExpression iteratedList = forComponent.getIteratedList();
if (hasProblem)
registerProblem(iteratedList, message.toString(), new ReplaceListComprehensionsQuickFix());
}
}
@Override
public void visitPyRaiseStatement(PyRaiseStatement node) {
super.visitPyRaiseStatement(node);
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
boolean hasNoArgs = UnsupportedFeaturesUtil.raiseHasNoArgs(node, languageLevel);
if (hasNoArgs) {
message.append(languageLevel).append(" ");
hasProblem = true;
}
}
message.append(" do not support this syntax. Raise with no arguments can only be used in an except block");
if (hasProblem)
registerProblem(node, message.toString());
hasProblem = false;
message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
boolean hasTwoArgs = UnsupportedFeaturesUtil.raiseHasMoreThenOneArg(node, languageLevel);
if (hasTwoArgs) {
if (hasProblem)
message.append(", ");
message.append(languageLevel);
hasProblem = true;
}
}
message.append(" do not support this syntax.");
if (hasProblem)
registerProblem(node, message.toString(), new ReplaceRaiseStatementQuickFix());
}
@Override
public void visitPyReprExpression(PyReprExpression node) {
super.visitPyReprExpression(node);
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (languageLevel.isPy3K()) {
if (hasProblem)
message.append(", ");
message.append(languageLevel);
hasProblem = true;
}
}
message.append(" do not support backquotes, use repr() instead");
if (hasProblem)
registerProblem(node, message.toString(), new ReplaceBackquoteExpressionQuickFix());
}
@Override
public void visitPyWithStatement(PyWithStatement node) {
super.visitPyWithStatement(node);
Set<PyWithItem> problemItems = new HashSet<PyWithItem>();
StringBuilder message = new StringBuilder(myCommonMessage);
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());
}
}
@Override
public void visitPyCallExpression(PyCallExpression node) {
super.visitPyCallExpression(node);
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (!languageLevel.isPy3K()) {
final PsiElement firstChild = node.getFirstChild();
if (firstChild != null) {
final String name = firstChild.getText();
if (PyNames.SUPER.equals(name)) {
final PyArgumentList argumentList = node.getArgumentList();
if (argumentList != null && argumentList.getArguments().length == 0) {
if (hasProblem)
message.append(", ");
message.append(languageLevel.toString());
hasProblem = true;
}
}
}
}
}
if (hasProblem)
registerProblem(node, message.toString());
hasProblem = false;
message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
PyExpression callee = node.getCallee();
assert callee != null;
PsiReference reference = callee.getReference();
if (reference != null) {
PsiElement resolved = reference.resolve();
ProjectFileIndex ind = ProjectRootManager.getInstance(callee.getProject()).getFileIndex();
if (resolved != null) {
PsiFile file = resolved.getContainingFile();
if (file != null && ind.isInLibraryClasses(file.getVirtualFile())) {
final String name = callee.getText();
if (!name.equals("print") && UnsupportedFeaturesUtil.BUILTINS.get(languageLevel).contains(name)) {
if (hasProblem)
message.append(", ");
hasProblem = true;
message.append(languageLevel.toString());
}
}
}
}
}
if (hasProblem) {
message.append(" have no method ").append(node.getCallee().getText());
registerProblem(node, message.toString());
}
}
@Override
public void visitPyClass(PyClass node) { //PY-2719
if (myVersionsToProcess.contains(LanguageLevel.PYTHON24)) {
PyArgumentList list = node.getSuperClassExpressionList();
if (list != null && list.getArguments().length == 0)
registerProblem(list, "Python version 2.4 does not support this syntax.");
}
}
@Override
public void visitPyPrintStatement(PyPrintStatement node) {
if (myVersionsToProcess.contains(LanguageLevel.PYTHON30) || myVersionsToProcess.contains(LanguageLevel.PYTHON31)) {
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 versions >= 3.0 do not support this syntax. The print statement has been replaced with a print() function",
new CompatibilityPrintCallQuickFix());
}
}
@Override
public void visitPyImportElement(PyImportElement node) {
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (UnsupportedFeaturesUtil.MODULES.get(languageLevel).contains(node.getText())) {
if (hasProblem)
message.append(", ");
message.append(languageLevel.toString());
hasProblem = true;
}
}
message.append(" do not have module ").append(node.getText());
if (hasProblem)
registerProblem(node, message.toString());
}
@Override
public void visitPyFromImportStatement(PyFromImportStatement node) {
boolean hasProblem = false;
StringBuilder message = new StringBuilder(myCommonMessage);
PyReferenceExpression importSource = node.getImportSource();
if (importSource != null) {
if (myVersionsToProcess.contains(LanguageLevel.PYTHON24)) { //PY-2793
PsiElement prev = importSource.getPrevSibling();
if (prev != null && prev.getNode().getElementType() == PyTokenTypes.DOT)
registerProblem(node, "Python version 2.4 doesn't support this syntax.");
}
String name = importSource.getText();
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (UnsupportedFeaturesUtil.MODULES.get(languageLevel).contains(name)) {
if (hasProblem)
message.append(", ");
message.append(languageLevel.toString());
hasProblem = true;
}
}
message.append(" do not have module ").append(name);
if (hasProblem)
registerProblem(node, message.toString());
}
else {
if (myVersionsToProcess.contains(LanguageLevel.PYTHON24))
registerProblem(node, "Python version 2.4 doesn't support this syntax.");
}
}
@Override
public void visitPyAssignmentStatement(PyAssignmentStatement node) {
if (myVersionsToProcess.contains(LanguageLevel.PYTHON24)) {
PyExpression assignedValue = node.getAssignedValue();
if (assignedValue instanceof PyConditionalExpression) // PY-2792
registerProblem(node, "Python version 2.4 doesn't support this syntax.");
Stack<PsiElement> st = new Stack<PsiElement>(); // PY-2796
if (assignedValue != null)
st.push(assignedValue);
while (!st.isEmpty()) {
PsiElement el = st.pop();
if (el instanceof PyYieldExpression)
registerProblem(node, "Python version 2.4 doesn't support this syntax. " +
"In Python <= 2.4, yield was a statement; it didn't return any value.");
else {
for (PsiElement e : el.getChildren())
st.push(e);
}
}
}
}
@Override
public void visitPyTryExceptStatement(PyTryExceptStatement node) { // PY-2795
if (myVersionsToProcess.contains(LanguageLevel.PYTHON24)) {
PyExceptPart[] excepts = node.getExceptParts();
PyFinallyPart finallyPart = node.getFinallyPart();
if (excepts.length != 0 && finallyPart != null)
registerProblem(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.");
protected final void registerProblem(@Nullable final PsiElement element,
@NotNull final String message,
final LocalQuickFix quickFix, boolean asError){
if (element == null || element.getTextLength() == 0){
return;
}
if (quickFix != null)
myHolder.registerProblem(element, message, quickFix);
else
myHolder.registerProblem(element, message);
}
}
}
@@ -0,0 +1,514 @@
package com.jetbrains.python.validation;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiWhiteSpace;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.actions.*;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
/**
* User : catherine
*/
public abstract class CompatibilityVisitor extends PyAnnotator {
protected List<LanguageLevel> myVersionsToProcess;
private String myCommonMessage = "Python version ";
public CompatibilityVisitor(List<LanguageLevel> versionsToProcess) {
myVersionsToProcess = versionsToProcess;
}
@Override
public void visitPyDictCompExpression(PyDictCompExpression node) {
super.visitPyDictCompExpression(node);
int len = 0;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (!languageLevel.supportsSetLiterals()) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
commonRegisterProblem(message, " not support dictionary comprehensions", len, node, new ConvertDictCompQuickFix(), false);
}
@Override
public void visitPySetLiteralExpression(PySetLiteralExpression node) {
super.visitPySetLiteralExpression(node);
int len = 0;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (!languageLevel.supportsSetLiterals()) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
commonRegisterProblem(message, " not support set literal expressions", len, node, new ConvertSetLiteralQuickFix(), false);
}
@Override
public void visitPySetCompExpression(PySetCompExpression node) {
super.visitPySetCompExpression(node);
int len = 0;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (!languageLevel.supportsSetLiterals()) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
commonRegisterProblem(message, " not support set comprehensions", len, node, null, false);
}
@Override
public void visitPyExceptBlock(PyExceptPart node) {
super.visitPyExceptBlock(node);
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, myCommonMessage + "2.4, 2.5 do not support this syntax.");
}
}
int len = 0;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (languageLevel.isPy3K()) {
PsiElement element = exceptClass.getNextSibling();
while (element instanceof PsiWhiteSpace) {
element = element.getNextSibling();
}
if (element != null && ",".equals(element.getText())) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
}
commonRegisterProblem(message, " not support this syntax.", len, node, new ReplaceExceptPartQuickFix());
}
}
@Override
public void visitPyImportStatement(PyImportStatement node) {
super.visitPyImportStatement(node);
PyImportElement[] importElements = node.getImportElements();
int len = 0;
String moduleName = "";
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
for (PyImportElement importElement : importElements) {
final PyQualifiedName qName = importElement.getImportedQName();
if (qName != null) {
if (!languageLevel.isPy3K()) {
if (qName.matches("builtins")) {
len = appendLanguageLevel(message, len, languageLevel);
moduleName = "builtins";
}
}
else {
if (qName.matches("__builtin__")) {
len = appendLanguageLevel(message, len, languageLevel);
moduleName = "__builtin__";
}
}
}
}
}
commonRegisterProblem(message, " not have module " + moduleName, len, node, new ReplaceBuiltinsQuickFix());
}
@Override
public void visitPyStarExpression(PyStarExpression node) {
super.visitPyStarExpression(node);
int len = 0;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (!languageLevel.isPy3K()) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
commonRegisterProblem(message, " not support this syntax. Starred expressions are not allowed as assignment targets in Python 2",
len, node, null);
}
@Override
public void visitPyBinaryExpression(PyBinaryExpression node) {
super.visitPyBinaryExpression(node);
int len = 0;
if (node.isOperator("<>")) {
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (languageLevel.isPy3K()) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
commonRegisterProblem(message, " not support <>, use != instead.", len, node, new ReplaceNotEqOperatorQuickFix());
}
}
@Override
public void visitPyNumericLiteralExpression(final PyNumericLiteralExpression node) {
super.visitPyNumericLiteralExpression(node);
int len = 0;
LocalQuickFix quickFix = null;
StringBuilder message = new StringBuilder(myCommonMessage);
String suffix = "";
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (languageLevel.isPy3K()) {
if (!node.isIntegerLiteral()) {
continue;
}
final String text = node.getText();
if (text.endsWith("l") || text.endsWith("L")) {
len = appendLanguageLevel(message, len, languageLevel);
suffix = " not support a trailing \'l\' or \'L\'.";
quickFix = new RemoveTrailingLQuickFix();
}
if (text.length() > 1 && text.charAt(0) == '0') {
final char c = Character.toLowerCase(text.charAt(1));
if (c != 'o' && c != 'b' && c != 'x') {
boolean isNull = true;
for (char a : text.toCharArray()) {
if ( a != '0') {
isNull = false;
break;
}
}
if (!isNull) {
len = appendLanguageLevel(message, len, languageLevel);
quickFix = new ReplaceOctalNumericLiteralQuickFix();
suffix = " not support this syntax. It requires '0o' prefix for octal literals";
}
}
}
}
}
commonRegisterProblem(message, suffix, len, node, quickFix);
}
@Override
public void visitPyStringLiteralExpression(final PyStringLiteralExpression node) {
super.visitPyStringLiteralExpression(node);
int len = 0;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (languageLevel.isPy3K()) {
final String text = node.getText();
if (text.startsWith("u") || text.startsWith("U")) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
}
commonRegisterProblem(message, " not support a leading \'u\' or \'U\'.", len, node, new RemoveLeadingUQuickFix());
}
@Override
public void visitPyListCompExpression(final PyListCompExpression node) {
super.visitPyListCompExpression(node);
int len = 0;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
boolean tmp = UnsupportedFeaturesUtil.visitPyListCompExpression(node, languageLevel);
if (tmp) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
for (ComprhForComponent forComponent : node.getForComponents()) {
final PyExpression iteratedList = forComponent.getIteratedList();
commonRegisterProblem(message, " not support this syntax in list comprehensions.", len, iteratedList,
new ReplaceListComprehensionsQuickFix());
}
}
@Override
public void visitPyRaiseStatement(PyRaiseStatement node) {
super.visitPyRaiseStatement(node);
int len = 0;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
boolean hasNoArgs = UnsupportedFeaturesUtil.raiseHasNoArgs(node, languageLevel);
if (hasNoArgs) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
commonRegisterProblem(message, " not support this syntax. Raise with no arguments can only be used in an except block",
len, node, null);
len = 0;
message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
boolean hasTwoArgs = UnsupportedFeaturesUtil.raiseHasMoreThenOneArg(node, languageLevel);
if (hasTwoArgs) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
commonRegisterProblem(message, " not support this syntax.",
len, node, new ReplaceRaiseStatementQuickFix());
}
@Override
public void visitPyReprExpression(PyReprExpression node) {
super.visitPyReprExpression(node);
int len = 0;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (languageLevel.isPy3K()) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
commonRegisterProblem(message, " not support backquotes, use repr() instead",
len, node, new ReplaceBackquoteExpressionQuickFix());
}
@Override
public void visitPyWithStatement(PyWithStatement node) {
super.visitPyWithStatement(node);
Set<PyWithItem> problemItems = new HashSet<PyWithItem>();
StringBuilder message = new StringBuilder(myCommonMessage);
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());
}
}
@Override
public void visitPyClass(PyClass node) { //PY-2719
super.visitPyClass(node);
if (myVersionsToProcess.contains(LanguageLevel.PYTHON24)) {
PyArgumentList list = node.getSuperClassExpressionList();
if (list != null && list.getArguments().length == 0)
registerProblem(list, "Python version 2.4 does not support this syntax.");
}
}
@Override
public void visitPyPrintStatement(PyPrintStatement node) {
super.visitPyPrintStatement(node);
if (myVersionsToProcess.contains(LanguageLevel.PYTHON30) || myVersionsToProcess.contains(LanguageLevel.PYTHON31)) {
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());
}
}
@Override
public void visitPyFromImportStatement(PyFromImportStatement node) {
super.visitPyFromImportStatement(node);
int len = 0;
StringBuilder message = new StringBuilder(myCommonMessage);
PyReferenceExpression importSource = node.getImportSource();
if (importSource != null) {
if (myVersionsToProcess.contains(LanguageLevel.PYTHON24)) { //PY-2793
PsiElement prev = importSource.getPrevSibling();
if (prev != null && prev.getNode().getElementType() == PyTokenTypes.DOT)
registerProblem(node, "Python version 2.4 doesn't support this syntax.");
}
String name = importSource.getText();
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (UnsupportedFeaturesUtil.MODULES.get(languageLevel).contains(name)) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
commonRegisterProblem(message, " not have module " + name,
len, node, null, false);
}
else {
if (myVersionsToProcess.contains(LanguageLevel.PYTHON24))
registerProblem(node, "Python version 2.4 doesn't support this syntax.");
}
}
@Override
public void visitPyAssignmentStatement(PyAssignmentStatement node) {
super.visitPyAssignmentStatement(node);
if (myVersionsToProcess.contains(LanguageLevel.PYTHON24)) {
PyExpression assignedValue = node.getAssignedValue();
if (assignedValue instanceof PyConditionalExpression) // PY-2792
registerProblem(node, "Python version 2.4 doesn't support this syntax.");
Stack<PsiElement> st = new Stack<PsiElement>(); // PY-2796
if (assignedValue != null)
st.push(assignedValue);
while (!st.isEmpty()) {
PsiElement el = st.pop();
if (el instanceof PyYieldExpression)
registerProblem(node, "Python version 2.4 doesn't support this syntax. " +
"In Python <= 2.4, yield was a statement; it didn't return any value.");
else {
for (PsiElement e : el.getChildren())
st.push(e);
}
}
}
}
@Override
public void visitPyTryExceptStatement(PyTryExceptStatement node) { // PY-2795
super.visitPyTryExceptStatement(node);
if (myVersionsToProcess.contains(LanguageLevel.PYTHON24)) {
PyExceptPart[] excepts = node.getExceptParts();
PyFinallyPart finallyPart = node.getFinallyPart();
if (excepts.length != 0 && finallyPart != null)
registerProblem(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.");
}
}
@Override
public void visitPyCallExpression(PyCallExpression node) {
super.visitPyCallExpression(node);
int len = 0;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (!languageLevel.isPy3K()) {
final PsiElement firstChild = node.getFirstChild();
if (firstChild != null) {
final String name = firstChild.getText();
if (PyNames.SUPER.equals(name)) {
final PyArgumentList argumentList = node.getArgumentList();
if (argumentList != null && argumentList.getArguments().length == 0) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
}
}
}
commonRegisterProblem(message, " not support this syntax. super() should have arguments in Python 2",
len, node, null);
len = 0;
message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
PyExpression callee = node.getCallee();
assert callee != null;
PsiReference reference = callee.getReference();
if (reference != null) {
PsiElement resolved = reference.resolve();
ProjectFileIndex ind = ProjectRootManager.getInstance(callee.getProject()).getFileIndex();
final String name = callee.getText();
if (resolved != null) {
PsiFile file = resolved.getContainingFile();
if (file != null && ind.isInLibraryClasses(file.getVirtualFile())) {
if (!name.equals("print") && UnsupportedFeaturesUtil.BUILTINS.get(languageLevel).contains(name)) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
}
else {
if (!name.equals("print") && UnsupportedFeaturesUtil.BUILTINS.get(languageLevel).contains(name)) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
}
}
commonRegisterProblem(message, " not have method " + node.getCallee().getText(),
len, node, null, false);
}
@Override
public void visitPyImportElement(PyImportElement node) {
super.visitPyImportElement(node);
int len = 0;
StringBuilder message = new StringBuilder(myCommonMessage);
for (int i = 0; i != myVersionsToProcess.size(); ++i) {
LanguageLevel languageLevel = myVersionsToProcess.get(i);
if (UnsupportedFeaturesUtil.MODULES.get(languageLevel).contains(node.getText()) &&
!node.getText().equals("__builtin__") && !node.getText().equals("builtins")) {
len = appendLanguageLevel(message, len, languageLevel);
}
}
commonRegisterProblem(message, " not have module " + node.getText(), len, node, null, false);
}
protected abstract void registerProblem(PsiElement node, String s, LocalQuickFix localQuickFix, boolean asError);
protected void registerProblem(PsiElement node, String s, LocalQuickFix localQuickFix) {
registerProblem(node, s, localQuickFix, true);
}
protected void registerProblem(PsiElement node, String s) {
registerProblem(node, s, null);
}
protected void setVersionsToProcess(List<LanguageLevel> versionsToProcess) {
myVersionsToProcess = versionsToProcess;
}
protected void commonRegisterProblem(StringBuilder initMessage, String suffix,
int len, PyElement node, LocalQuickFix localQuickFix) {
commonRegisterProblem(initMessage, suffix, len, node, localQuickFix, true);
}
protected void commonRegisterProblem(StringBuilder initMessage, String suffix,
int len, PyElement node, LocalQuickFix localQuickFix, boolean asError) {
initMessage.append(" do");
if (len == 1)
initMessage.append("es");
initMessage.append(suffix);
if (len != 0)
registerProblem(node, initMessage.toString(), localQuickFix, asError);
}
private static int appendLanguageLevel(StringBuilder message, int len, LanguageLevel languageLevel) {
if (len != 0)
message.append(", ");
message.append(languageLevel.toString());
return ++len;
}
}
@@ -9,24 +9,44 @@ import com.intellij.codeInspection.ex.ProblemDescriptorImpl;
import com.intellij.codeInspection.ex.QuickFixWrapper;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiWhiteSpace;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.actions.*;
import com.jetbrains.python.codeInsight.intentions.RemoveTrailingLIntention;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyElement;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Stack;
import java.util.ArrayList;
import java.util.Arrays;
/**
* @author Alexey.Ivanov
*/
public class UnsupportedFeatures extends PyAnnotator {
public class UnsupportedFeatures extends CompatibilityVisitor {
public UnsupportedFeatures() {
super(new ArrayList<LanguageLevel>());
}
@Override
public void visitPyElement(PyElement node) {
setVersionsToProcess(Arrays.asList(getLanguageLevel(node)));
}
@Override
protected void registerProblem(PsiElement node, String message, LocalQuickFix localQuickFix, boolean asError) {
if (node == null || node.getTextLength() == 0){
return;
}
if (localQuickFix != null)
if (asError)
getHolder().createErrorAnnotation(node, message).registerFix(createIntention(node, message, localQuickFix));
else
getHolder().createWarningAnnotation(node, message).registerFix(createIntention(node, message, localQuickFix));
else
if (asError)
getHolder().createErrorAnnotation(node, message);
else
getHolder().createWarningAnnotation(node, message);
}
@NotNull
private static LanguageLevel getLanguageLevel(PyElement node) {
VirtualFile virtualFile = node.getContainingFile().getVirtualFile();
@@ -36,321 +56,11 @@ public class UnsupportedFeatures extends PyAnnotator {
return LanguageLevel.getDefault();
}
private static boolean isPy2(PyElement node) {
return !getLanguageLevel(node).isPy3K();
}
private static boolean isPy3K(PyElement node) {
return getLanguageLevel(node).isPy3K();
}
private static IntentionAction createIntention(PyElement node, String message, LocalQuickFix fix) {
private static IntentionAction createIntention(PsiElement node, String message, LocalQuickFix fix) {
LocalQuickFix[] quickFixes = {fix};
CommonProblemDescriptorImpl descr = new ProblemDescriptorImpl(node, node, message,
quickFixes, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, true,
node.getTextRange(), true);
return QuickFixWrapper.wrap((ProblemDescriptor)descr, 0);
}
@Override
public void visitPyDictCompExpression(PyDictCompExpression node) {
super.visitPyDictCompExpression(node);
LanguageLevel languageLevel = getLanguageLevel(node);
if (!languageLevel.supportsSetLiterals()) {
String message = "Python version " + languageLevel + " does not support dictionary comprehensions";
getHolder()
.createWarningAnnotation(node, message)
.registerFix(createIntention(node, message, new ConvertDictCompQuickFix()));
}
}
@Override
public void visitPySetLiteralExpression(PySetLiteralExpression node) {
super.visitPySetLiteralExpression(node);
LanguageLevel languageLevel = getLanguageLevel(node);
if (!languageLevel.supportsSetLiterals()) {
String message = "Python version " + languageLevel + " does not support set literal expressions";
getHolder()
.createWarningAnnotation(node, message)
.registerFix(createIntention(node, message, new ConvertSetLiteralQuickFix()));
}
}
@Override
public void visitPySetCompExpression(PySetCompExpression node) {
super.visitPySetCompExpression(node);
final LanguageLevel languageLevel = getLanguageLevel(node);
if (!languageLevel.supportsSetLiterals()) {
getHolder().createWarningAnnotation(node, "Python version " + languageLevel + " does not support set comprehensions");
}
}
@Override
public void visitPyExceptBlock(PyExceptPart node) {
super.visitPyExceptBlock(node);
PyExpression exceptClass = node.getExceptClass();
if (exceptClass != null) {
LanguageLevel languageLevel = getLanguageLevel(node);
if (languageLevel == LanguageLevel.PYTHON24 || languageLevel == LanguageLevel.PYTHON25) {
PsiElement element = exceptClass.getNextSibling();
while (element instanceof PsiWhiteSpace) {
element = element.getNextSibling();
}
if (element != null && "as".equals(element.getText())) {
getHolder().createWarningAnnotation(node, "This Python version does not support this syntax");
}
}
else if (isPy3K(node)) {
PsiElement element = exceptClass.getNextSibling();
while (element instanceof PsiWhiteSpace) {
element = element.getNextSibling();
}
if (element != null && ",".equals(element.getText())) {
String message = "Python 3 does not support this syntax";
getHolder().createWarningAnnotation(node, message).registerFix(createIntention(node, message, new ReplaceExceptPartQuickFix()));
}
}
}
}
@Override
public void visitPyImportStatement(PyImportStatement node) {
super.visitPyImportStatement(node);
PyImportElement[] importElements = node.getImportElements();
for (PyImportElement importElement : importElements) {
final PyQualifiedName qName = importElement.getImportedQName();
if (qName != null) {
if (isPy2(node)) {
if (qName.matches("builtins")) {
String message = "There is no module builtins in Python 2";
getHolder().createWarningAnnotation(node, message)
.registerFix(createIntention(node, message, new ReplaceBuiltinsQuickFix()));
}
}
else {
if (qName.matches("__builtin__")) {
String message = "Module __builtin__ renamed to builtins";
getHolder().createWarningAnnotation(node, "Module __builtin__ renamed to builtins")
.registerFix(createIntention(node, message, new ReplaceBuiltinsQuickFix()));
}
}
}
}
}
@Override
public void visitPyCallExpression(PyCallExpression node) {
super.visitPyCallExpression(node);
if (isPy2(node)) {
final PsiElement firstChild = node.getFirstChild();
if (firstChild != null) {
final String name = firstChild.getText();
if (PyNames.SUPER.equals(name)) {
final PyArgumentList argumentList = node.getArgumentList();
if (argumentList != null && argumentList.getArguments().length == 0) {
getHolder().createWarningAnnotation(node, "super() should have arguments in Python 2");
}
}
}
}
else {
PyExpression callee = node.getCallee();
assert callee != null;
PsiReference reference = callee.getReference();
if (reference != null) {
PsiElement resolved = reference.resolve();
if (resolved == null) {
final String name = callee.getText();
if (!name.equals("print") && UnsupportedFeaturesUtil.BUILTINS.get(getLanguageLevel(callee)).contains(name)) {
getHolder().createWarningAnnotation(callee, PyBundle.message("ANN.method.$0.removed", name));
}
}
}
}
}
@Override
public void visitPyStarExpression(PyStarExpression node) {
super.visitPyStarExpression(node);
if (isPy2(node)) {
getHolder().createWarningAnnotation(node, "Starred expressions are not allowed as assignment targets in Python 2");
}
}
@Override
public void visitPyBinaryExpression(PyBinaryExpression node) {
super.visitPyBinaryExpression(node);
if (isPy3K(node) && node.isOperator("<>")) {
final String message = isPy3K(node) ? "<> is not supported in Python 3, use != instead" : "<> is deprecated, use != instead";
getHolder().createWarningAnnotation(node, message).registerFix(createIntention(node, message, new ReplaceNotEqOperatorQuickFix()));
}
}
@Override
public void visitPyNumericLiteralExpression(final PyNumericLiteralExpression node) {
super.visitPyNumericLiteralExpression(node);
if (isPy3K(node)) {
if (!node.isIntegerLiteral()) {
return;
}
final String text = node.getText();
if (text.endsWith("l") || text.endsWith("L")) {
getHolder().createWarningAnnotation(node,
"Integer literals do not support a trailing \'l\' or \'L\' in Python 3")
.registerFix(new RemoveTrailingLIntention());
}
if (text.length() > 1 && text.charAt(0) == '0') {
final char c = Character.toLowerCase(text.charAt(1));
if (c != 'o' && c != 'b' && c != 'x') {
boolean isNull = true;
for (char a : text.toCharArray()) {
if ( a != '0') {
isNull = false;
break;
}
}
if (!isNull) {
final String message = "Python 3 requires '0o' prefix for octal literals";
getHolder().createWarningAnnotation(node, message).registerFix(createIntention(node, message, new ReplaceOctalNumericLiteralQuickFix()));
}
}
}
}
}
@Override
public void visitPyStringLiteralExpression(final PyStringLiteralExpression node) {
super.visitPyStringLiteralExpression(node);
if (isPy3K(node)) {
final String text = node.getText();
if (text.startsWith("u") || text.startsWith("U")) {
final String message = "String literals do not support a leading \'u\' or \'U\' in Python 3";
getHolder().createWarningAnnotation(node, message).registerFix(createIntention(node, message, new RemoveLeadingUQuickFix()));
}
}
}
@Override
public void visitPyListCompExpression(final PyListCompExpression node) {
super.visitPyListCompExpression(node);
if (isPy3K(node)) {
final List<ComprhForComponent> forComponents = node.getForComponents();
for (ComprhForComponent forComponent : forComponents) {
final PyExpression iteratedList = forComponent.getIteratedList();
if (iteratedList instanceof PyTupleExpression) {
final String message = "List comprehensions do not support this syntax in Python 3";
getHolder().createWarningAnnotation(iteratedList, message).registerFix(createIntention(iteratedList, message, new ReplaceListComprehensionsQuickFix()));
}
}
}
}
@Override
public void visitPyRaiseStatement(PyRaiseStatement node) {
super.visitPyRaiseStatement(node);
boolean hasProblem = UnsupportedFeaturesUtil.raiseHasNoArgs(node, LanguageLevel.forElement(node));
if (hasProblem) {
getHolder().createErrorAnnotation(node, "raise with no arguments can only be used in an except block");
}
hasProblem = UnsupportedFeaturesUtil.raiseHasMoreThenOneArg(node, LanguageLevel.forElement(node));
if (hasProblem) {
final String message = "Python 3 does not support this syntax";
getHolder().createWarningAnnotation(node, message).registerFix(createIntention(node, message, new ReplaceRaiseStatementQuickFix()));
}
}
@Override
public void visitPyReprExpression(PyReprExpression node) {
super.visitPyReprExpression(node);
if (isPy3K(node)) {
final String message = "Backquote is not supported in Python 3, use repr() instead";
getHolder().createWarningAnnotation(node, message).registerFix(createIntention(node, message, new ReplaceBackquoteExpressionQuickFix()));
}
}
@Override
public void visitPyWithStatement(PyWithStatement node) {
super.visitPyWithStatement(node);
final LanguageLevel languageLevel = getLanguageLevel(node);
if (languageLevel == LanguageLevel.PYTHON24) {
getHolder().createWarningAnnotation(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 i = 1; i < items.length; i++) {
getHolder().createWarningAnnotation(items [i], "Python version " + languageLevel + " does not support multiple context managers");
}
}
}
}
@Override
public void visitPyClass(PyClass node) { //PY-2719
if (getLanguageLevel(node) == LanguageLevel.PYTHON24) {
PyArgumentList list = node.getSuperClassExpressionList();
if (list != null && list.getArguments().length == 0)
getHolder().createWarningAnnotation(list, "Python version 2.4 does not support this syntax.");
}
}
@Override
public void visitPyPrintStatement(PyPrintStatement node) {
PsiElement[] arguments = node.getChildren();
if (getLanguageLevel(node).isPy3K()) {
for (PsiElement element : arguments) {
if (!((element instanceof PyParenthesizedExpression) || (element instanceof PyTupleExpression)))
getHolder().createWarningAnnotation(element, "Python versions >= 3.0 do not support this syntax. The print statement has been replaced with a print() function");
}
}
}
@Override
public void visitPyFromImportStatement(PyFromImportStatement node) {
PyReferenceExpression importSource = node.getImportSource();
if (getLanguageLevel(node) == LanguageLevel.PYTHON24) {
if (importSource == null)
getHolder().createWarningAnnotation(node, "Python version 2.4 doesn't support this syntax.");
else {
PsiElement prev = importSource.getPrevSibling();
if (prev != null && prev.getNode().getElementType() == PyTokenTypes.DOT)
getHolder().createWarningAnnotation(node, "Python version 2.4 doesn't support this syntax.");
}
}
}
@Override
public void visitPyAssignmentStatement(PyAssignmentStatement node) {
if (getLanguageLevel(node) == LanguageLevel.PYTHON24) {
PyExpression assignedValue = node.getAssignedValue();
if (assignedValue instanceof PyConditionalExpression) // PY-2792
getHolder().createWarningAnnotation(node, "Python version 2.4 doesn't support this syntax.");
Stack<PsiElement> st = new Stack<PsiElement>(); // PY-2796
if (assignedValue != null)
st.push(assignedValue);
while (!st.isEmpty()) {
PsiElement el = st.pop();
if (el instanceof PyYieldExpression)
getHolder().createWarningAnnotation(node, "Python version 2.4 doesn't support this syntax. " +
"In Python <= 2.4, yield was a statement; it didn't return any value.");
else {
for (PsiElement e : el.getChildren())
st.push(e);
}
}
}
}
@Override
public void visitPyTryExceptStatement(PyTryExceptStatement node) { // PY-2795
if (getLanguageLevel(node) == LanguageLevel.PYTHON24) {
PyExceptPart[] excepts = node.getExceptParts();
PyFinallyPart finallyPart = node.getFinallyPart();
if (excepts.length != 0 && finallyPart != null)
getHolder().createWarningAnnotation(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.");
}
}
}
@@ -1,24 +1,24 @@
print(<warning descr="<> is not supported in Python 3, use != instead">a <> 3</warning>)
<warning descr="Backquote is not supported in Python 3, use repr() instead">`foo()`</warning>
a = <warning descr="Integer literals do not support a trailing 'l' or 'L' in Python 3">123l</warning>
a = <warning descr="Python 3 requires '0o' prefix for octal literals">043</warning>
print(<error descr="Python version 3.0 does not support <>, use != instead.">a <> 3</error>)
<error descr="Python version 3.0 does not support backquotes, use repr() instead">`foo()`</error>
a = <error descr="Python version 3.0 does not support a trailing 'l' or 'L'.">123l</error>
a = <error descr="Python version 3.0 does not support this syntax. It requires '0o' prefix for octal literals">043</error>
a = 0X43
a = 0b1
a = 0.0
s = <warning descr="String literals do not support a leading 'u' or 'U' in Python 3">u"text"</warning>
<warning descr="Python 3 does not support this syntax">raise a, b, c</warning>
<warning descr="Python 3 does not support this syntax">raise a, b</warning>
s = <error descr="Python version 3.0 does not support a leading 'u' or 'U'.">u"text"</error>
<error descr="Python version 3.0 does not support this syntax.">raise a, b, c</error>
<error descr="Python version 3.0 does not support this syntax.">raise a, b</error>
try:
pass
<warning descr="Python 3 does not support this syntax">except a, name:
pass</warning>
<error descr="Python version 3.0 does not support this syntax.">except a, name:
pass</error>
[x * 2 for x in <warning descr="List comprehensions do not support this syntax in Python 3">vec1, vec2</warning>]
[x * 2 for x in <error descr="Python version 3.0 does not support this syntax in list comprehensions.">vec1, vec2</error>]
<warning descr="Module __builtin__ renamed to builtins">import __builtin__</warning>
<error descr="Python version 3.0 does not have module __builtin__">import __builtin__</error>
<error descr="raise with no arguments can only be used in an except block">raise</error>
<error descr="Python version 3.0 does not support this syntax. Raise with no arguments can only be used in an except block">raise</error>
try:
pass
@@ -0,0 +1,5 @@
# PY-2792
<warning descr="Python version 2.4 doesn't support this syntax.">x = True if condition else False</warning>
def foo(): # PY-2796
<warning descr="Python version 2.4 doesn't support this syntax. In Python <= 2.4, yield was a statement; it didn't return any value.">a = (yield 1)</warning>
@@ -0,0 +1,4 @@
print(<warning descr="Python version 3.0, 3.1 do not support <>, use != instead.">a <> b</warning>)
if <warning descr="Python version 3.0, 3.1 do not support <>, use != instead.">a <> 2</warning>:
var = a
@@ -0,0 +1,11 @@
class A(B):
def __init__(self):
<warning descr="Python version 2.4, 2.5, 2.6, 2.7 do not support this syntax. super() should have arguments in Python 2">super()</warning>
<warning descr="Python version 3.1 does not have method cmp">cmp()</warning>
<warning descr="Python version 3.0, 3.1 do not have method reduce"><warning descr="Python version 3.0 does not have method reduce">reduce()</warning></warning>
<warning descr="Python version 2.4 does not have method all">all()</warning>
<warning descr="Python version 2.4, 2.5, 2.6, 2.7 do not have method bytearray">bytearray()</warning>
<warning descr="Python version 2.4, 2.5 do not have method next">next()</warning>
<warning descr="Python version 2.4, 2.5, 3.0, 3.1 do not have method buffer"><warning descr="Python version 3.0 does not have method buffer">buffer()</warning></warning>
@@ -0,0 +1 @@
var = <warning descr="Python version 2.4, 2.5, 2.6, 3.0 do not support dictionary comprehensions">{i : chr(65+i) for i in range(4)}</warning>
@@ -0,0 +1,9 @@
try:
do_smth()
<warning descr="Python version 2.4, 2.5 do not support this syntax.">except ImportError as e:
do()</warning>
try:
do_smth()
<warning descr="Python version 3.0, 3.1 do not support this syntax.">except ImportError, ImportWarning:
do()</warning>
@@ -0,0 +1,4 @@
<warning descr="Python version 2.4 doesn't support this syntax.">from . import smth</warning>
<warning descr="Python version 2.4 doesn't support this syntax.">from .module import name1, name2</warning>
<warning descr="Python version 3.0, 3.1 do not have module Bastion">from Bastion import BastionClass</warning>
@@ -0,0 +1 @@
import <warning descr="Python version 3.0, 3.1 do not have module Bastion">Bastion</warning>
@@ -0,0 +1,3 @@
<error descr="Python version 2.7 does not have module builtins"><warning descr="Python version 2.4, 2.5, 2.6, 2.7 do not have module builtins">import builtins</warning></error>
<warning descr="Python version 3.0, 3.1 do not have module __builtin__">import __builtin__</warning>
@@ -0,0 +1 @@
var = [x for x in <warning descr="Python version 3.0, 3.1 do not support this syntax in list comprehensions.">1, 2, 3</warning>]
@@ -0,0 +1,2 @@
a = <warning descr="Python version 3.0, 3.1 do not support a trailing 'l' or 'L'.">12l</warning>
v = <warning descr="Python version 3.0, 3.1 do not support this syntax. It requires '0o' prefix for octal literals">04</warning><error descr="end of statement expected">8</error>
@@ -0,0 +1 @@
<warning descr="Python version >= 3.0 do not support this syntax. The print statement has been replaced with a print() function">print "One value"</warning>
@@ -0,0 +1,3 @@
#PY-2719
class BaseC<warning descr="Python version 2.4 does not support this syntax.">()</warning>:
pass
@@ -0,0 +1,6 @@
try:
a
except :
<warning descr="Python version 3.0, 3.1 do not support this syntax.">raise ImportError, ImportWarning</warning>
<warning descr="Python version 3.0, 3.1 do not support this syntax. Raise with no arguments can only be used in an except block">raise</warning>
@@ -0,0 +1 @@
a = <warning descr="Python version 3.0, 3.1 do not support backquotes, use repr() instead">`imp.acquire_lock()`</warning>
@@ -0,0 +1 @@
var = <warning descr="Python version 2.4, 2.5, 2.6, 3.0 do not support set comprehensions">{i for i in range(2)}</warning>
@@ -0,0 +1 @@
var = <warning descr="Python version 2.4, 2.5, 2.6, 3.0 do not support set literal expressions">{1, 2}</warning>
@@ -0,0 +1,2 @@
t = (1, 2, 3)
a, <warning descr="Python version 2.4, 2.5, 2.6, 2.7 do not support this syntax. Starred expressions are not allowed as assignment targets in Python 2">*b</warning>, c = t
@@ -0,0 +1 @@
a = <warning descr="Python version 3.0, 3.1 do not support a leading 'u' or 'U'.">u"String"</warning>
@@ -1,10 +1,10 @@
print(<warning descr="Python versions 3.0, 3.1 do not support <>, use != instead.">a <> b</warning>)
print(<warning descr="Python version 3.0, 3.1 do not support <>, use != instead.">a <> b</warning>)
if <warning descr="Python versions 3.0, 3.1 do not support <>, use != instead.">a <> 2</warning>:
if <warning descr="Python version 3.0, 3.1 do not support <>, use != instead.">a <> 2</warning>:
var = a
########################
<warning descr="Python versions >= 3.0 do not support this syntax. The print statement has been replaced with a print() function">print "One value"</warning>
<warning descr="Python version >= 3.0 do not support this syntax. The print statement has been replaced with a print() function">print "One value"</warning>
########################
@@ -14,14 +14,14 @@ class BaseC<warning descr="Python version 2.4 does not support this syntax.">()<
########################
<warning descr="Python version 2.4 doesn't support this syntax.">with A() as a, <warning descr="Python versions 2.5, 2.6, 3.0 do not support multiple context managers">B() as b</warning>:
<warning descr="Python version 2.4 doesn't support this syntax.">with A() as a, <warning descr="Python version 2.5, 2.6, 3.0 do not support multiple context managers">B() as b</warning>:
suite
</warning>########################
a = <warning descr="Python versions 3.0, 3.1 do not support backquotes, use repr() instead">`imp.acquire_lock()`</warning>
a = <warning descr="Python version 3.0, 3.1 do not support backquotes, use repr() instead">`imp.acquire_lock()`</warning>
########################
var = [x for x in <warning descr="Python versions 3.0, 3.1 do not support this syntax in list comprehensions.">1, 2, 3</warning>]
var = [x for x in <warning descr="Python version 3.0, 3.1 do not support this syntax in list comprehensions.">1, 2, 3</warning>]
########################
class A:
@@ -33,35 +33,35 @@ a.cmp()
########################
<warning descr="Python versions 3.1 have no method cmp">cmp()</warning>
<warning descr="Python versions 3.0, 3.1 have no method reduce">reduce()</warning>
<warning descr="Python versions 2.4 have no method all">all()</warning>
<warning descr="Python version 3.1 does not have method cmp">cmp()</warning>
<warning descr="Python version 3.0, 3.1 do not have method reduce">reduce()</warning>
<warning descr="Python version 2.4 does not have method all">all()</warning>
bytearray()
next()
<warning descr="Python versions 2.4, 2.5, 3.0, 3.1 have no method buffer">buffer()</warning>
<warning descr="Python version 2.4, 2.5, 3.0, 3.1 do not have method buffer">buffer()</warning>
########################
try:
a
except :
<warning descr="Python versions 3.0, 3.1 do not support this syntax.">raise ImportError, ImportWarning</warning>
<warning descr="Python version 3.0, 3.1 do not support this syntax.">raise ImportError, ImportWarning</warning>
try:
a
<warning descr="Python versions 3.0, 3.1 do not this syntax.">except ImportError, ImportWarning:
<warning descr="Python version 3.0, 3.1 do not support this syntax.">except ImportError, ImportWarning:
b
</warning>########################
var = <warning descr="Python versions 2.4, 2.5, 2.6, 3.0 do not support dictionary comprehensions">{i : chr(65+i) for i in range(4)}</warning>
var = <warning descr="Python version 2.4, 2.5, 2.6, 3.0 do not support dictionary comprehensions">{i : chr(65+i) for i in range(4)}</warning>
########################
import <warning descr="Python versions 3.0, 3.1 do not have module Bastion">Bastion</warning>
import <warning descr="Python version 3.0, 3.1 do not have module Bastion">Bastion</warning>
var = Bastion.BastionClass()
########################
@@ -70,7 +70,7 @@ def foo(): # PY-2796
########################
<warning descr="Python versions 3.0 3.1 do not support this syntax. Raise with no arguments can only be used in an except block">raise</warning>
<warning descr="Python version 3.0, 3.1 do not support this syntax. re with no arguments can only be used in an except block">raise</warning>
########################
# PY-2792
@@ -0,0 +1,8 @@
<warning descr="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.">try:
do_smth()
except ImportError:
do()
except KeyError:
do_1()
finally:
quit()</warning>
@@ -0,0 +1,2 @@
<warning descr="Python version 2.4 doesn't support this syntax.">with A() as a, <warning descr="Python version 2.5, 2.6, 3.0 do not support multiple context managers">B() as b</warning>:
suite</warning>
@@ -3,8 +3,8 @@ def foo():
print x
def test_vlu():
<warning descr="Starred expressions are not allowed as assignment targets in Python 2">*<warning descr="Local variable 'h' value is not used">h</warning></warning>, <warning descr="Local variable 't' value is not used">t</warning> = [1, 2, 3] # fail
<error descr="Python version 2.6 does not support this syntax. Starred expressions are not allowed as assignment targets in Python 2">*<warning descr="Local variable 'h' value is not used">h</warning></error>, <warning descr="Local variable 't' value is not used">t</warning> = [1, 2, 3] # fail
def test_vlu():
<warning descr="Starred expressions are not allowed as assignment targets in Python 2">*h</warning>, t = [1, 2, 3] # pass
<error descr="Python version 2.6 does not support this syntax. Starred expressions are not allowed as assignment targets in Python 2">*h</error>, t = [1, 2, 3] # pass
print(t)
@@ -1 +1 @@
print(<warning descr="Python versions 3.0, 3.1 do not support <>, use != instead.">a<caret> <> b</warning>)
print(<warning descr="Python version 3.0, 3.1 do not support <>, use != instead.">a<caret> <> b</warning>)
@@ -74,6 +74,7 @@ public class PythonHighlightingTest extends PyLightFixtureTestCase {
}
public void testAssignmentTargets() {
setLanguageLevel(LanguageLevel.PYTHON26);
doTest(true, false);
}
@@ -0,0 +1,102 @@
package com.jetbrains.python.inspections;
import com.jetbrains.python.fixtures.PyLightFixtureTestCase;
import com.jetbrains.python.psi.LanguageLevel;
/**
* User : catherine
*/
public class PyCompatibilityInspectionTest extends PyLightFixtureTestCase {
public void testDictCompExpression() {
setLanguageLevel(LanguageLevel.PYTHON27);
doTest();
}
public void testSetLiteralExpression() {
setLanguageLevel(LanguageLevel.PYTHON27);
doTest();
}
public void testSetCompExpression() {
setLanguageLevel(LanguageLevel.PYTHON27);
doTest();
}
public void testExceptBlock() {
doTest();
}
public void testImportStatement() {
doTest();
}
public void testStarExpression() {
setLanguageLevel(LanguageLevel.PYTHON30);
doTest();
}
public void testBinaryExpression() {
setLanguageLevel(LanguageLevel.PYTHON27);
doTest();
}
public void testNumericLiteralExpression() {
doTest();
}
public void testStringLiteralExpression() {
doTest();
}
public void testListCompExpression() {
doTest();
}
public void testRaiseStatement() {
doTest();
}
public void testReprExpression() {
doTest();
}
public void testWithStatement() {
doTest();
}
public void testPyClass() {
doTest();
}
public void testPrintStatement() {
doTest();
}
public void testFromImportStatement() {
doTest();
}
public void testAssignmentStatement() {
doTest();
}
public void testTryExceptStatement() {
doTest();
}
public void testImportElement() {
doTest();
}
public void testCallExpression() {
setLanguageLevel(LanguageLevel.PYTHON30);
doTest();
}
private void doTest() {
myFixture.configureByFile("inspections/PyCompatibilityInspection/" + getTestName(true) + ".py");
myFixture.enableInspections(PyCompatibilityInspection.class);
myFixture.checkHighlighting(true, false, false);
}
}