diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index 3f73c06ccebf..1549b9600ba1 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -129,6 +129,8 @@ QFIX.NAME.remove.dict.key=Remove this key QFIX.NAME.add.specifier=Add format specifier character +QFIX.NAME.add.exception.base=Add Exception base class + # Intentions: INTN INTN.Family.convert.import.unqualify=Convert 'import module' to 'from module import' INTN.Family.convert.import.qualify=Convert 'from module import' to 'import module' diff --git a/python/src/com/jetbrains/python/inspections/PyExceptionInheritInspection.java b/python/src/com/jetbrains/python/inspections/PyExceptionInheritInspection.java index 6ece46395030..b659cb8b0caf 100644 --- a/python/src/com/jetbrains/python/inspections/PyExceptionInheritInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyExceptionInheritInspection.java @@ -19,7 +19,9 @@ import com.intellij.codeInspection.LocalInspectionToolSession; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiPolyVariantReference; import com.jetbrains.python.PyBundle; +import com.jetbrains.python.inspections.quickfix.PyAddExceptionSuperClassQuickFix; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.types.PyClassLikeType; import org.jetbrains.annotations.Nls; @@ -60,7 +62,9 @@ public class PyExceptionInheritInspection extends PyInspection { if (expression instanceof PyCallExpression) { PyExpression callee = ((PyCallExpression)expression).getCallee(); if (callee instanceof PyReferenceExpression) { - PsiElement psiElement = ((PyReferenceExpression)callee).getReference(getResolveContext()).resolve(); + final PsiPolyVariantReference reference = ((PyReferenceExpression)callee).getReference(getResolveContext()); + if (reference == null) return; + PsiElement psiElement = reference.resolve(); if (psiElement instanceof PyClass) { PyClass aClass = (PyClass) psiElement; for (PyClassLikeType type : aClass.getAncestorTypes(myTypeEvalContext)) { @@ -72,7 +76,7 @@ public class PyExceptionInheritInspection extends PyInspection { return; } } - registerProblem(expression, "Exception doesn't inherit from base \'Exception\' class"); + registerProblem(expression, "Exception doesn't inherit from base \'Exception\' class", new PyAddExceptionSuperClassQuickFix()); } } } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyAddExceptionSuperClassQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyAddExceptionSuperClassQuickFix.java new file mode 100644 index 000000000000..00d1f6d4611b --- /dev/null +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyAddExceptionSuperClassQuickFix.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.python.inspections.quickfix; + +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.lang.ASTNode; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiPolyVariantReference; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.psi.*; +import org.jetbrains.annotations.NotNull; + +public class PyAddExceptionSuperClassQuickFix implements LocalQuickFix { + + @NotNull + public String getName() { + return PyBundle.message("QFIX.NAME.add.exception.base"); + } + + @NotNull + public String getFamilyName() { + return getName(); + } + + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement(); + if (element instanceof PyCallExpression) { + PyExpression callee = ((PyCallExpression)element).getCallee(); + if (callee instanceof PyReferenceExpression) { + final PsiPolyVariantReference reference = ((PyReferenceExpression)callee).getReference(); + PsiElement psiElement = reference.resolve(); + if (psiElement instanceof PyClass) { + final PyElementGenerator generator = PyElementGenerator.getInstance(project); + final PyArgumentList list = ((PyClass)psiElement).getSuperClassExpressionList(); + if (list != null) { + final PyExpression exception = + generator.createExpressionFromText(LanguageLevel.forElement(element), "Exception"); + list.addArgument(exception); + } + else { + final PyArgumentList expressionList = generator.createFromText( + LanguageLevel.forElement(element), PyClass.class, "class A(Exception): pass").getSuperClassExpressionList(); + assert expressionList != null; + final ASTNode nameNode = ((PyClass)psiElement).getNameNode(); + assert nameNode != null; + psiElement.addAfter(expressionList, nameNode.getPsi()); + } + } + } + } + } + +} diff --git a/python/testData/quickFixes/PyAddExceptionSuperClassQuickFixTest/emptySuperList.py b/python/testData/quickFixes/PyAddExceptionSuperClassQuickFixTest/emptySuperList.py new file mode 100644 index 000000000000..1d4b461388dc --- /dev/null +++ b/python/testData/quickFixes/PyAddExceptionSuperClassQuickFixTest/emptySuperList.py @@ -0,0 +1,9 @@ + +class MyException: + def __new__(cls, x): + pass + + +def foo(): + raise MyException() + diff --git a/python/testData/quickFixes/PyAddExceptionSuperClassQuickFixTest/emptySuperList_after.py b/python/testData/quickFixes/PyAddExceptionSuperClassQuickFixTest/emptySuperList_after.py new file mode 100644 index 000000000000..3804bf9461f2 --- /dev/null +++ b/python/testData/quickFixes/PyAddExceptionSuperClassQuickFixTest/emptySuperList_after.py @@ -0,0 +1,9 @@ + +class MyException(Exception): + def __new__(cls, x): + pass + + +def foo(): + raise MyException() + diff --git a/python/testData/quickFixes/PyAddExceptionSuperClassQuickFixTest/nonEmptySuperList.py b/python/testData/quickFixes/PyAddExceptionSuperClassQuickFixTest/nonEmptySuperList.py new file mode 100644 index 000000000000..81c7263ede1f --- /dev/null +++ b/python/testData/quickFixes/PyAddExceptionSuperClassQuickFixTest/nonEmptySuperList.py @@ -0,0 +1,12 @@ + +class A: + pass + +class MyException(A): + def __new__(cls, x): + pass + + +def foo(): + raise MyException() + diff --git a/python/testData/quickFixes/PyAddExceptionSuperClassQuickFixTest/nonEmptySuperList_after.py b/python/testData/quickFixes/PyAddExceptionSuperClassQuickFixTest/nonEmptySuperList_after.py new file mode 100644 index 000000000000..c92e761d5234 --- /dev/null +++ b/python/testData/quickFixes/PyAddExceptionSuperClassQuickFixTest/nonEmptySuperList_after.py @@ -0,0 +1,12 @@ + +class A: + pass + +class MyException(A, Exception): + def __new__(cls, x): + pass + + +def foo(): + raise MyException() + diff --git a/python/testSrc/com/jetbrains/python/quickFixes/PyAddExceptionSuperClassQuickFixTest.java b/python/testSrc/com/jetbrains/python/quickFixes/PyAddExceptionSuperClassQuickFixTest.java new file mode 100644 index 000000000000..b8a432da36d4 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/quickFixes/PyAddExceptionSuperClassQuickFixTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.python.quickFixes; + +import com.intellij.testFramework.TestDataPath; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.PyQuickFixTestCase; +import com.jetbrains.python.inspections.PyExceptionInheritInspection; + +/** + * User: ktisha + */ +@TestDataPath("$CONTENT_ROOT/../testData//quickFixes/PyAddExceptionSuperClassQuickFixTest/") +public class PyAddExceptionSuperClassQuickFixTest extends PyQuickFixTestCase { + + public void testEmptySuperList() { + doQuickFixTest(PyExceptionInheritInspection.class, PyBundle.message("QFIX.NAME.add.exception.base")); + } + + public void testNonEmptySuperList() { + doQuickFixTest(PyExceptionInheritInspection.class, PyBundle.message("QFIX.NAME.add.exception.base")); + } + +}