added update property signature quick fix to the Property definitions inspection

This commit is contained in:
Ekaterina Tuzova
2014-03-13 12:13:06 +04:00
parent 016663e8cb
commit 71cbc6e3ed
12 changed files with 200 additions and 18 deletions

View File

@@ -129,6 +129,8 @@ QFIX.NAME.remove.assignment=Remove assignment
QFIX.NAME.remove.statement=Remove statement
QFIX.NAME.update.parameters=Update parameters
QFIX.NAME.remove.call=Remove call
QFIX.NAME.move.except.up=Move except clause up

View File

@@ -24,12 +24,14 @@ import com.intellij.openapi.util.Comparing;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiPolyVariantReference;
import com.intellij.psi.util.PsiElementFilter;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.QualifiedName;
import com.intellij.util.Processor;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.inspections.quickfix.PyUpdatePropertySignatureQuickFix;
import com.jetbrains.python.inspections.quickfix.RenameParameterQuickFix;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
@@ -143,13 +145,16 @@ public class PyPropertyDefinitionInspection extends PyInspection {
assert argument != null : "Parameter mapped to null argument";
Callable callable = null;
if (argument instanceof PyReferenceExpression) {
PsiElement resolved = ((PyReferenceExpression)argument).getReference(getResolveContext()).resolve();
if (resolved instanceof Callable) {
callable = (Callable)resolved;
}
else {
reportNonCallableArg(resolved, argument);
return;
final PsiPolyVariantReference reference = ((PyReferenceExpression)argument).getReference(getResolveContext());
if (reference != null) {
PsiElement resolved = reference.resolve();
if (resolved instanceof Callable) {
callable = (Callable)resolved;
}
else {
reportNonCallableArg(resolved, argument);
return;
}
}
}
else if (argument instanceof PyLambdaExpression) callable = (PyLambdaExpression)argument;
@@ -244,7 +249,7 @@ public class PyPropertyDefinitionInspection extends PyInspection {
// signature: at least two params, more optionals ok; first arg 'self'
final PyParameterList param_list = callable.getParameterList();
if (myTwoParamFunction != null && !PyUtil.isSignatureCompatibleTo(callable, myTwoParamFunction, myTypeEvalContext)) {
registerProblem(being_checked, PyBundle.message("INSP.setter.signature.advice"));
registerProblem(being_checked, PyBundle.message("INSP.setter.signature.advice"), new PyUpdatePropertySignatureQuickFix(true));
}
checkForSelf(param_list);
// no explicit return type
@@ -259,13 +264,18 @@ public class PyPropertyDefinitionInspection extends PyInspection {
}
}
private void checkOneParameter(Callable callable, PsiElement being_checked, boolean is_getter) {
final PyParameterList param_list = callable.getParameterList();
private void checkOneParameter(Callable callable, PsiElement beingChecked, boolean isGetter) {
final PyParameterList parameterList = callable.getParameterList();
if (myOneParamFunction != null && !PyUtil.isSignatureCompatibleTo(callable, myOneParamFunction, myTypeEvalContext)) {
if (is_getter) registerProblem(being_checked, PyBundle.message("INSP.getter.signature.advice"));
else registerProblem(being_checked, PyBundle.message("INSP.deleter.signature.advice"));
if (isGetter) {
registerProblem(beingChecked, PyBundle.message("INSP.getter.signature.advice"),
new PyUpdatePropertySignatureQuickFix(false));
}
else {
registerProblem(beingChecked, PyBundle.message("INSP.deleter.signature.advice"), new PyUpdatePropertySignatureQuickFix(false));
}
}
checkForSelf(param_list);
checkForSelf(parameterList);
}
private void checkForSelf(PyParameterList param_list) {
@@ -302,11 +312,9 @@ public class PyPropertyDefinitionInspection extends PyInspection {
if (allowed && callable instanceof PyFunction) {
// one last chance: maybe there's no return but a 'raise' statement, see PY-4043, PY-5048
PyStatementList statementList = ((PyFunction)callable).getStatementList();
if (statementList != null) {
for (PyStatement stmt : statementList.getStatements()) {
if (stmt instanceof PyRaiseStatement) {
return;
}
for (PyStatement stmt : statementList.getStatements()) {
if (stmt instanceof PyRaiseStatement) {
return;
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
public class PyUpdatePropertySignatureQuickFix implements LocalQuickFix {
private final boolean myHasValue;
public PyUpdatePropertySignatureQuickFix(boolean hasValue) {
myHasValue = hasValue;
}
@NotNull
@Override
public String getName() {
return PyBundle.message("QFIX.NAME.update.parameters");
}
@NotNull
public String getFamilyName() {
return getName();
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
final PyFunction function = PsiTreeUtil.getParentOfType(element, PyFunction.class);
assert function != null;
final PyParameterList parameterList = function.getParameterList();
final PyParameter[] parameters = parameterList.getParameters();
final PyElementGenerator generator = PyElementGenerator.getInstance(project);
String selfText = parameters.length != 0 ? parameters[0].getText() : PyNames.CANONICAL_SELF;
final StringBuilder functionText = new StringBuilder("def foo(" + selfText);
if (myHasValue) {
String valueText = parameters.length > 1 ? parameters[1].getText() : "value";
functionText.append(", ").append(valueText);
}
functionText.append("): pass");
final PyParameterList list = generator.createFromText(LanguageLevel.forElement(element),
PyFunction.class, functionText.toString()).getParameterList();
parameterList.replace(list);
}
}

View File

@@ -0,0 +1,8 @@
class A(Aa):
@property
def <warning descr="Getter signature should be (self)">x<caret></warning>(self, r):
return ""
@x.setter
def <warning descr="Setter should not return a value">x</warning>(self, r):
return r

View File

@@ -0,0 +1,8 @@
class A(Aa):
@property
def <warning descr="Getter signature should be (self)">x<caret></warning>():
return ""
@x.setter
def <warning descr="Setter should not return a value">x</warning>(self, r):
return r

View File

@@ -0,0 +1,8 @@
class A(Aa):
@property
def x(self):
return ""
@x.setter
def x(self, r):
return r

View File

@@ -0,0 +1,8 @@
class A(Aa):
@property
def x(self):
return ""
@x.setter
def x(self, r):
return r

View File

@@ -0,0 +1,8 @@
class A(Aa):
@property
def <warning descr="Getter signature should be (self)">x</warning>(self, r):
return r
@x.setter
def <warning descr="Setter signature should be (self, value)"><caret>x</warning>(self, r, t):
self._x = ""

View File

@@ -0,0 +1,8 @@
class A(Aa):
@property
def <warning descr="Getter signature should be (self)">x</warning>(self, r):
return r
@x.setter
def <warning descr="Setter signature should be (self, value)">x<caret></warning>():
self._x = ""

View File

@@ -0,0 +1,8 @@
class A(Aa):
@property
def x(self, r):
return r
@x.setter
def x(self, value):
self._x = ""

View File

@@ -0,0 +1,8 @@
class A(Aa):
@property
def x(self, r):
return r
@x.setter
def x(self, r):
self._x = ""

View File

@@ -0,0 +1,42 @@
/*
* 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.PyPropertyDefinitionInspection;
@TestDataPath("$CONTENT_ROOT/../testData//quickFixes/PyUpdatePropertySignatureQuickFixTest/")
public class PyUpdatePropertySignatureQuickFixTest extends PyQuickFixTestCase {
public void testGetter() {
doQuickFixTest(PyPropertyDefinitionInspection.class, PyBundle.message("QFIX.NAME.update.parameters"));
}
public void testGetterNoPararm() {
doQuickFixTest(PyPropertyDefinitionInspection.class, PyBundle.message("QFIX.NAME.update.parameters"));
}
public void testSetter() {
doQuickFixTest(PyPropertyDefinitionInspection.class, PyBundle.message("QFIX.NAME.update.parameters"));
}
public void testSetterLessParam() {
doQuickFixTest(PyPropertyDefinitionInspection.class, PyBundle.message("QFIX.NAME.update.parameters"));
}
}