Merge remote-tracking branch 'origin/master'

This commit is contained in:
Dmitry Trofimov
2013-03-25 20:24:30 +01:00
34 changed files with 249 additions and 91 deletions

View File

@@ -34,7 +34,10 @@ class TeamcityDocTestResult(TeamcityTestResult):
def getTestId(self, test):
file = os.path.realpath(self.current_suite.filename)
return "file://" + file + ":" + str( self.current_suite.lineno + test.lineno)
line_no = test.lineno
if self.current_suite.lineno:
line_no += self.current_suite.lineno
return "file://" + file + ":" + str(line_no)
def getSuiteLocation(self):
file = os.path.realpath(self.current_suite.filename)

View File

@@ -0,0 +1,64 @@
/*
* 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.psi;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFileSystemItem;
import com.intellij.psi.impl.source.resolve.reference.impl.CachingReference;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceHelper;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* This is a temporary fix for FileReference.bindToElement to take contexts from FileReference.getContexts() instead for helpers.
*
* @author traff
*/
public class FileReferenceWithOneContext extends FileReference {
public FileReferenceWithOneContext(@NotNull FileReferenceSet fileReferenceSet,
TextRange range, int index, String text) {
super(fileReferenceSet, range, index, text);
}
public FileReferenceWithOneContext(FileReference original) {
super(original);
}
@Override
protected Collection<PsiFileSystemItem> getContextsForBindToElement(VirtualFile curVFile, Project project, FileReferenceHelper helper) {
return getContexts();
}
@Override
protected PsiElement rename(final String newName) throws IncorrectOperationException {
if (FileUtil.isAbsolutePlatformIndependent(newName)) {
return super.rename(newName);
}
else {
PsiElement element = getElement();
return CachingReference.getManipulator(element).handleContentChange(element, getRangeInElement(), newName);
}
}
}

View File

@@ -25,7 +25,7 @@ import org.jetbrains.annotations.Nullable;
/**
* @author yole
*/
public class WeakFileReference extends FileReference implements PsiReferenceEx {
public class WeakFileReference extends FileReferenceWithOneContext implements PsiReferenceEx {
public WeakFileReference(FileReferenceSet fileReferenceSet, TextRange range, int index, String text) {
super(fileReferenceSet, range, index, text);
}

View File

@@ -82,7 +82,7 @@ public class TemplateFileReference extends WeakFileReference {
dstItem = _dstItem;
}
final Collection<PsiFileSystemItem> contexts = getFileReferenceSet().getDefaultContexts();
final Collection<PsiFileSystemItem> contexts = getContexts();
switch (contexts.size()) {
case 0:
break;

View File

@@ -25,7 +25,7 @@ QFIX.NAME.parameters=Parameters of functions and methods
QFIX.rename.parameter.to.$0=Rename to ''{0}''
QFIX.NAME.add.field.$0.to.class.$1=Add field ''{0}'' to class {1}
QFIX.added.constructor.$0.for.field.$1=Added a constructor to class <code>{0}</code><br/>to accommodate new field <code>{1}</code>
QFIX.added.constructor.$0.for.field.$1=Added a __init__ to class <code>{0}</code><br/>to accommodate new field <code>{1}</code>
QFIX.failed.to.add.field=<br/>Failed to add a field!<br/><br/>
QFIX.NAME.add.method.$0.to.class.$1=Add method {0}() to class {1}
@@ -460,7 +460,8 @@ INSP.NAME.single.quoted.docstring=Single quoted docstring
INSP.message.single.quoted.docstring=Triple double-quoted strings should be used for docstrings.
# PyMissingConstructorInspection
INSP.NAME.missing.super.constructor=Missed call to constructor of super class
INSP.NAME.missing.super.constructor=Missed call to __init__ of super class
INSP.missing.super.constructor.message=Call to __init__ of super class is missed
# PySetFunctionToLiteralInspection
INSP.NAME.set.function.to.literal=Function call can be replaced with set literal

View File

@@ -6,10 +6,11 @@ import com.intellij.psi.PsiElementVisitor;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.inspections.quickfix.PyMoveAttributeToInitQuickFix;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyTargetExpression;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyCallExpressionHelper;
import com.jetbrains.python.psi.impl.PyClassImpl;
import com.jetbrains.python.psi.types.PyClassType;
import com.jetbrains.python.psi.types.PyType;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -52,9 +53,12 @@ public class PyAttributeOutsideInitInspection extends PyInspection {
Map<String, PyTargetExpression> attributesInInit = new HashMap<String, PyTargetExpression>();
final PyFunction initMethod = containingClass.findMethodByName(PyNames.INIT, false);
if (initMethod != null)
if (initMethod != null) {
PyClassImpl.collectInstanceAttributes(initMethod, attributesInInit);
collectAttributesFromSuper(attributesInInit, initMethod);
}
Map<String, PyTargetExpression> attributes = new HashMap<String, PyTargetExpression>();
PyClassImpl.collectInstanceAttributes(node, attributes);
@@ -66,5 +70,25 @@ public class PyAttributeOutsideInitInspection extends PyInspection {
}
}
private void collectAttributesFromSuper(Map<String, PyTargetExpression> attributesInInit, PyFunction initMethod) {
final PyStatementList statementList = initMethod.getStatementList();
if (statementList != null) {
for (PyStatement statement : statementList.getStatements()) {
if (statement instanceof PyExpressionStatement) {
final PyExpression expression = ((PyExpressionStatement)statement).getExpression();
if (expression instanceof PyCallExpression) {
final PyType callType = PyCallExpressionHelper.getCallType((PyCallExpression)expression, myTypeEvalContext);
if (callType instanceof PyClassType) {
final PyClass superClass = ((PyClassType)callType).getPyClass();
final PyFunction superInit = superClass.findMethodByName(PyNames.INIT, false);
if (superInit != null) {
PyClassImpl.collectInstanceAttributes(superInit, attributesInInit);
}
}
}
}
}
}
}
}
}

View File

@@ -54,10 +54,10 @@ public class PyMissingConstructorInspection extends PyInspection {
return;
}
if (superClasses.length == 1 || node.isNewStyleClass())
registerProblem(initMethod.getNameIdentifier(), "Call to constructor of super class is missed",
registerProblem(initMethod.getNameIdentifier(), PyBundle.message("INSP.missing.super.constructor.message"),
new AddCallSuperQuickFix(node.getSuperClasses()[0], superClasses[0].getText()));
else
registerProblem(initMethod.getNameIdentifier(), "Call to constructor of super class is missed");
registerProblem(initMethod.getNameIdentifier(), PyBundle.message("INSP.missing.super.constructor.message"));
}
}

View File

@@ -700,7 +700,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
actions.add(new AddMethodQuickFix(refText, (PyClassType)qtype, true));
}
else if (!(reference instanceof PyOperatorReference)) {
actions.add(new AddFieldQuickFix(refText, cls, "None"));
actions.add(new AddFieldQuickFix(refText, (PyClassType)qtype, "None"));
}
}
}

View File

@@ -27,6 +27,7 @@ import com.jetbrains.python.psi.impl.PyImportStatementNavigator;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.search.PyOverridingMethodsSearch;
import com.jetbrains.python.psi.search.PySuperMethodsSearch;
import com.jetbrains.python.psi.types.PyClassTypeImpl;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -280,7 +281,7 @@ public class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor {
}
}
final LocalQuickFix[] fixes = mayBeField
? new LocalQuickFix[] { new AddFieldQuickFix(name, containingClass, name) }
? new LocalQuickFix[] { new AddFieldQuickFix(name, new PyClassTypeImpl(containingClass, false), name) }
: LocalQuickFix.EMPTY_ARRAY;
registerWarning(element, PyBundle.message("INSP.unused.locals.parameter.isnot.used", name), fixes);
}

View File

@@ -14,6 +14,7 @@ import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import com.jetbrains.python.psi.types.PyClassType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -24,19 +25,19 @@ import org.jetbrains.annotations.Nullable;
*/
public class AddFieldQuickFix implements LocalQuickFix {
private PyClass myQualifierClass;
private PyClassType myQualifierType;
private final String myInitializer;
private String myIdentifier;
public AddFieldQuickFix(String identifier, PyClass qualifierClass, String initializer) {
public AddFieldQuickFix(String identifier, PyClassType qualifierType, String initializer) {
myIdentifier = identifier;
myQualifierClass = qualifierClass;
myQualifierType = qualifierType;
myInitializer = initializer;
}
@NotNull
public String getName() {
return PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", myIdentifier, myQualifierClass.getName());
return PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", myIdentifier, myQualifierType.getName());
}
@NotNull
@@ -66,14 +67,19 @@ public class AddFieldQuickFix implements LocalQuickFix {
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
// expect the descriptor to point to the unresolved identifier.
PyClass cls = myQualifierClass;
String item_name = myIdentifier;
if (cls != null) {
PsiElement initStatement = addFieldToInit(project, cls, item_name, new CreateFieldCallback(project, item_name, myInitializer));
if (initStatement != null) {
showTemplateBuilder(initStatement);
return;
}
PyClass cls = myQualifierType.getPyClass();
PsiElement initStatement;
if (!myQualifierType.isDefinition()) {
initStatement = addFieldToInit(project, cls, myIdentifier, new CreateFieldCallback(project, myIdentifier, myInitializer));
}
else {
PyStatement field = PyElementGenerator.getInstance(project)
.createFromText(LanguageLevel.getDefault(), PyStatement.class, myIdentifier + " = " + myInitializer);
initStatement = PyUtil.addElementToStatementList(field, cls.getStatementList(), true);
}
if (initStatement != null) {
showTemplateBuilder(initStatement);
return;
}
// somehow we failed. tell about this
PyUtil.showBalloon(project, PyBundle.message("QFIX.failed.to.add.field"), MessageType.ERROR);
@@ -83,14 +89,17 @@ public class AddFieldQuickFix implements LocalQuickFix {
initStatement = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(initStatement);
if (initStatement instanceof PyAssignmentStatement) {
final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(initStatement);
builder.replaceElement(((PyAssignmentStatement) initStatement).getAssignedValue(), myInitializer);
builder.run();
final PyExpression assignedValue = ((PyAssignmentStatement)initStatement).getAssignedValue();
if (assignedValue != null) {
builder.replaceElement(assignedValue, myInitializer);
builder.run();
}
}
}
@Nullable
public static PsiElement addFieldToInit(Project project, PyClass cls, String item_name, Function<String, PyStatement> callback) {
if (cls != null && item_name != null) {
public static PsiElement addFieldToInit(Project project, PyClass cls, String itemName, Function<String, PyStatement> callback) {
if (cls != null && itemName != null) {
PyFunction init = cls.findMethodByName(PyNames.INIT, false);
if (init != null) {
return appendToMethod(init, callback);
@@ -100,21 +109,23 @@ public class AddFieldQuickFix implements LocalQuickFix {
init = ancestor.findMethodByName(PyNames.INIT, false);
if (init != null) break;
}
PyFunction new_init = createInitMethod(project, cls, init);
if (new_init == null) {
PyFunction newInit = createInitMethod(project, cls, init);
if (newInit == null) {
return null;
}
appendToMethod(new_init, callback);
appendToMethod(newInit, callback);
PsiElement add_anchor = null;
PsiElement addAnchor = null;
PyFunction[] meths = cls.getMethods();
if (meths.length > 0) add_anchor = meths[0].getPrevSibling();
PyStatementList cls_content = cls.getStatementList();
new_init = (PyFunction) cls_content.addAfter(new_init, add_anchor);
if (meths.length > 0) addAnchor = meths[0].getPrevSibling();
PyStatementList clsContent = cls.getStatementList();
newInit = (PyFunction) clsContent.addAfter(newInit, addAnchor);
PyUtil.showBalloon(project, PyBundle.message("QFIX.added.constructor.$0.for.field.$1", cls.getName(), item_name), MessageType.INFO);
return new_init.getStatementList().getStatements()[0];
PyUtil.showBalloon(project, PyBundle.message("QFIX.added.constructor.$0.for.field.$1", cls.getName(), itemName), MessageType.INFO);
final PyStatementList statementList = newInit.getStatementList();
assert statementList != null;
return statementList.getStatements()[0];
//else // well, that can't be
}
}

View File

@@ -113,7 +113,7 @@ public class AddMethodQuickFix implements LocalQuickFix {
meth.addBefore(deco_list, meth.getFirstChild()); // in the very beginning
}
meth = (PyFunction)PyUtil.addElementToStatementList(meth, cls_stmt_list);
meth = (PyFunction)PyUtil.addElementToStatementList(meth, cls_stmt_list, PyNames.INIT.equals(meth.getName()));
if (myReplaceUsage)
showTemplateBuilder(meth);
}

View File

@@ -5,8 +5,9 @@ import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.intellij.util.FunctionUtil;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -39,31 +40,11 @@ public class PyMoveAttributeToInitQuickFix implements LocalQuickFix {
final PyAssignmentStatement assignment = PsiTreeUtil.getParentOfType(element, PyAssignmentStatement.class);
if (containingClass == null || assignment == null) return;
final PsiElement copy = assignment.copy();
if (!addDefinition(copy, containingClass)) return;
final Function<String, PyStatement> callback = FunctionUtil.<String, PyStatement>constant(assignment);
AddFieldQuickFix.addFieldToInit(project, containingClass, ((PyTargetExpression)element).getName(), callback);
removeDefinition(assignment);
}
private static boolean addDefinition(PsiElement copy, PyClass containingClass) {
PyFunction init = containingClass.findMethodByName(PyNames.INIT, false);
if (init == null) {
final PyStatementList classStatementList = containingClass.getStatementList();
init = PyElementGenerator.getInstance(containingClass.getProject()).createFromText(LanguageLevel.forElement(containingClass),
PyFunction.class,
"def __init__(self):\n\t" +
copy.getText());
PyUtil.addElementToStatementList(init, classStatementList);
return true;
}
final PyStatementList statementList = init.getStatementList();
if (statementList == null) return false;
PyUtil.addElementToStatementList(copy, statementList);
return true;
}
private static boolean removeDefinition(PyAssignmentStatement assignment) {
final PyStatementList statementList = PsiTreeUtil.getParentOfType(assignment, PyStatementList.class);
if (statementList == null) return false;

View File

@@ -1203,15 +1203,16 @@ public class PyUtil {
}
public static PsiElement addElementToStatementList(@NotNull PsiElement element, @NotNull PyStatementList statementList) {
public static PsiElement addElementToStatementList(@NotNull PsiElement element,
@NotNull PyStatementList statementList,
boolean toTheBeginning) {
final PsiElement firstChild = statementList.getFirstChild();
if (firstChild == statementList.getLastChild() && firstChild instanceof PyPassStatement) {
element = firstChild.replace(element);
}
else {
final PyStatement[] statements = statementList.getStatements();
String name = element instanceof PsiNamedElement ? ((PsiNamedElement)element).getName() : "";
if (PyNames.INIT.equals(name) && statements.length > 0) {
if (toTheBeginning && statements.length > 0) {
final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(statementList, PyDocStringOwner.class);
final PyStatement firstStatement = statements[0];
if (docStringOwner != null && firstStatement instanceof PyExpressionStatement &&

View File

@@ -51,11 +51,12 @@ public class PyPathEvaluator {
if (argValue == null) {
return null;
}
if (FileUtil.isAbsolute(argValue)) {
if (FileUtil.isAbsolutePlatformIndependent(argValue)) {
return argValue;
}
else {
return new File(new File(containingFilePath).getParent(), argValue).getPath();
String path = new File(new File(containingFilePath).getParent(), argValue).getPath();
return path.replace("\\", "/");
}
}
else if (call.isCalleeText(PyNames.REPLACE) && args.length == 2) {
@@ -108,7 +109,7 @@ public class PyPathEvaluator {
result = arg;
}
else {
result = new File(result, arg).getPath();
result = new File(result, arg).getPath().replace("\\", "/");
}
}
return result;

View File

@@ -3,5 +3,5 @@ class A:
pass
class B(A):
def <caret><warning descr="Call to constructor of super class is missed">__init__</warning>(self, r, b = 6, *args, **kwargs):
def <caret><warning descr="Call to __init__ of super class is missed">__init__</warning>(self, r, b = 6, *args, **kwargs):
print "Constructor B was called"

View File

@@ -3,6 +3,6 @@ class A:
pass
class B(A):
def <caret><warning descr="Call to constructor of super class is missed">__init__</warning>(self, r, b = 6):
def <caret><warning descr="Call to __init__ of super class is missed">__init__</warning>(self, r, b = 6):
"""docstring"""
print "Constructor B was called"

View File

@@ -3,5 +3,5 @@ class A:
pass
class B(A):
def <caret><warning descr="Call to constructor of super class is missed">__init__</warning>(self, r, b = 6):
def <caret><warning descr="Call to __init__ of super class is missed">__init__</warning>(self, r, b = 6):
pass

View File

@@ -0,0 +1,12 @@
__author__ = 'ktisha'
class Base(object):
def __init__(self):
self.my = 1
class Child(Base):
def __init__(self):
super(Child, self).__init__()
def f(self):
self.my = 1

View File

@@ -0,0 +1,8 @@
__author__ = 'ktisha'
class A(object):
def method(self):
var = A.<warning descr="Unresolved attribute reference 'FIELD' for class 'A'"><caret>FIELD</warning>
var = self.<warning descr="Unresolved attribute reference 'test' for class 'A'">test</warning>

View File

@@ -0,0 +1,10 @@
__author__ = 'ktisha'
class A(object):
FIELD = None
def method(self):
var = A.FIELD
var = self.test

View File

@@ -0,0 +1,9 @@
__author__ = 'ktisha'
class Base(object):
def __init__(self):
self.param = 2
class Child(Base):
def f(self):
self.<caret>my = 2

View File

@@ -0,0 +1,13 @@
__author__ = 'ktisha'
class Base(object):
def __init__(self):
self.param = 2
class Child(Base):
def __init__(self):
super(Child, self).__init__()
self.my = 2
def f(self):
pass

View File

@@ -95,26 +95,6 @@ public class PyQuickFixTest extends PyTestCase {
true);
}
public void testAddFieldFromMethod() {
doInspectionTest("AddFieldFromMethod.py", PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "y", "A"),
true, true);
}
public void testAddFieldFromInstance() {
doInspectionTest("AddFieldFromInstance.py", PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "y", "A"),
true, true);
}
public void testAddFieldAddConstructor() {
doInspectionTest("AddFieldAddConstructor.py", PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "x", "B"),
true, true);
}
public void testAddFieldNewConstructor() {
doInspectionTest("AddFieldNewConstructor.py", PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "x", "B"),
true, true);
}
public void testRemoveTrailingSemicolon() {
doInspectionTest("RemoveTrailingSemicolon.py", PyTrailingSemicolonInspection.class, PyBundle.message("QFIX.remove.trailing.semicolon"),
true, true);

View File

@@ -15,6 +15,10 @@ public class PyAttributeOutsideInitInspectionTest extends PyTestCase {
doTest();
}
public void testDefinedInSuperClass() {
doTest();
}
private void doTest() {
myFixture.configureByFile("inspections/PyAttributeOutsideInitInspection/" + getTestName(true) + ".py");
myFixture.enableInspections(PyAttributeOutsideInitInspection.class);

View File

@@ -0,0 +1,31 @@
package com.jetbrains.python.quickFixes;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection;
/**
* User: ktisha
*/
public class AddFieldQuickFixTest extends PyQuickFixTestCase {
public void testAddClassField() {
doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "FIELD", "A"));
}
public void testAddFieldFromMethod() {
doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "y", "A"));
}
public void testAddFieldFromInstance() {
doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "y", "A"));
}
public void testAddFieldAddConstructor() {
doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "x", "B"));
}
public void testAddFieldNewConstructor() {
doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "x", "B"));
}
}

View File

@@ -30,4 +30,8 @@ public class PyMoveAttributeToInitQuickFixTest extends PyQuickFixTestCase {
doInspectionTest(PyAttributeOutsideInitInspection.class, PyBundle.message("QFIX.move.attribute"));
}
public void testAddSuperCall() {
doInspectionTest(PyAttributeOutsideInitInspection.class, PyBundle.message("QFIX.move.attribute"));
}
}