From 9750faf20d83214cfae41cccc541a052adddb67b Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Fri, 22 Mar 2013 18:13:13 +0400 Subject: [PATCH 1/8] fixed PY-9253 Broken "add field to class" adds the field to the instance instead. moved tests to the separate testCase --- .../PyUnresolvedReferencesInspection.java | 2 +- .../PyUnusedLocalInspectionVisitor.java | 3 +- .../quickfix/AddFieldQuickFix.java | 37 ++++++++++++------- .../quickfix/AddMethodQuickFix.java | 2 +- .../PyMoveAttributeToInitQuickFix.java | 4 +- .../src/com/jetbrains/python/psi/PyUtil.java | 7 ++-- .../AddFieldQuickFixTest/addClassField.py | 8 ++++ .../addClassField_after.py | 10 +++++ .../addFieldAddConstructor.py} | 0 .../addFieldAddConstructor_after.py} | 0 .../addFieldFromInstance.py} | 0 .../addFieldFromInstance_after.py} | 0 .../addFieldFromMethod.py} | 0 .../addFieldFromMethod_after.py} | 0 .../addFieldNewConstructor.py} | 0 .../addFieldNewConstructor_after.py} | 0 .../addPass_after.py | 2 +- .../moveToInit_after.py | 2 +- .../com/jetbrains/python/PyQuickFixTest.java | 20 ---------- .../quickFixes/AddFieldQuickFixTest.java | 31 ++++++++++++++++ 20 files changed, 84 insertions(+), 44 deletions(-) create mode 100644 python/testData/quickFixes/AddFieldQuickFixTest/addClassField.py create mode 100644 python/testData/quickFixes/AddFieldQuickFixTest/addClassField_after.py rename python/testData/{inspections/AddFieldAddConstructor.py => quickFixes/AddFieldQuickFixTest/addFieldAddConstructor.py} (100%) rename python/testData/{inspections/AddFieldAddConstructor_after.py => quickFixes/AddFieldQuickFixTest/addFieldAddConstructor_after.py} (100%) rename python/testData/{inspections/AddFieldFromInstance.py => quickFixes/AddFieldQuickFixTest/addFieldFromInstance.py} (100%) rename python/testData/{inspections/AddFieldFromInstance_after.py => quickFixes/AddFieldQuickFixTest/addFieldFromInstance_after.py} (100%) rename python/testData/{inspections/AddFieldFromMethod.py => quickFixes/AddFieldQuickFixTest/addFieldFromMethod.py} (100%) rename python/testData/{inspections/AddFieldFromMethod_after.py => quickFixes/AddFieldQuickFixTest/addFieldFromMethod_after.py} (100%) rename python/testData/{inspections/AddFieldNewConstructor.py => quickFixes/AddFieldQuickFixTest/addFieldNewConstructor.py} (100%) rename python/testData/{inspections/AddFieldNewConstructor_after.py => quickFixes/AddFieldQuickFixTest/addFieldNewConstructor_after.py} (100%) create mode 100644 python/testSrc/com/jetbrains/python/quickFixes/AddFieldQuickFixTest.java diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index da29e94bd1f3..12a6dd165bdd 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -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")); } } } diff --git a/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java b/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java index 5aae8e6ec16b..d5aa057d3887 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java +++ b/python/src/com/jetbrains/python/inspections/PyUnusedLocalInspectionVisitor.java @@ -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); } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java index 0475429fa7cc..44e1e05fe308 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java @@ -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,8 +89,11 @@ 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(); + } } } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddMethodQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddMethodQuickFix.java index 0b4e34859e66..7892b9348bba 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddMethodQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddMethodQuickFix.java @@ -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); } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyMoveAttributeToInitQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyMoveAttributeToInitQuickFix.java index c18e697b7e09..5eae3b68ab2e 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyMoveAttributeToInitQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyMoveAttributeToInitQuickFix.java @@ -54,13 +54,13 @@ public class PyMoveAttributeToInitQuickFix implements LocalQuickFix { PyFunction.class, "def __init__(self):\n\t" + copy.getText()); - PyUtil.addElementToStatementList(init, classStatementList); + PyUtil.addElementToStatementList(init, classStatementList, true); return true; } final PyStatementList statementList = init.getStatementList(); if (statementList == null) return false; - PyUtil.addElementToStatementList(copy, statementList); + PyUtil.addElementToStatementList(copy, statementList, true); return true; } diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 9b72ceb55c05..429a3d4f9aab 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -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 && diff --git a/python/testData/quickFixes/AddFieldQuickFixTest/addClassField.py b/python/testData/quickFixes/AddFieldQuickFixTest/addClassField.py new file mode 100644 index 000000000000..f6372db19419 --- /dev/null +++ b/python/testData/quickFixes/AddFieldQuickFixTest/addClassField.py @@ -0,0 +1,8 @@ +__author__ = 'ktisha' + +class A(object): + + def method(self): + var = A.FIELD + var = self.test + diff --git a/python/testData/quickFixes/AddFieldQuickFixTest/addClassField_after.py b/python/testData/quickFixes/AddFieldQuickFixTest/addClassField_after.py new file mode 100644 index 000000000000..908cf4007452 --- /dev/null +++ b/python/testData/quickFixes/AddFieldQuickFixTest/addClassField_after.py @@ -0,0 +1,10 @@ +__author__ = 'ktisha' + +class A(object): + + FIELD = None + + def method(self): + var = A.FIELD + var = self.test + diff --git a/python/testData/inspections/AddFieldAddConstructor.py b/python/testData/quickFixes/AddFieldQuickFixTest/addFieldAddConstructor.py similarity index 100% rename from python/testData/inspections/AddFieldAddConstructor.py rename to python/testData/quickFixes/AddFieldQuickFixTest/addFieldAddConstructor.py diff --git a/python/testData/inspections/AddFieldAddConstructor_after.py b/python/testData/quickFixes/AddFieldQuickFixTest/addFieldAddConstructor_after.py similarity index 100% rename from python/testData/inspections/AddFieldAddConstructor_after.py rename to python/testData/quickFixes/AddFieldQuickFixTest/addFieldAddConstructor_after.py diff --git a/python/testData/inspections/AddFieldFromInstance.py b/python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromInstance.py similarity index 100% rename from python/testData/inspections/AddFieldFromInstance.py rename to python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromInstance.py diff --git a/python/testData/inspections/AddFieldFromInstance_after.py b/python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromInstance_after.py similarity index 100% rename from python/testData/inspections/AddFieldFromInstance_after.py rename to python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromInstance_after.py diff --git a/python/testData/inspections/AddFieldFromMethod.py b/python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromMethod.py similarity index 100% rename from python/testData/inspections/AddFieldFromMethod.py rename to python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromMethod.py diff --git a/python/testData/inspections/AddFieldFromMethod_after.py b/python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromMethod_after.py similarity index 100% rename from python/testData/inspections/AddFieldFromMethod_after.py rename to python/testData/quickFixes/AddFieldQuickFixTest/addFieldFromMethod_after.py diff --git a/python/testData/inspections/AddFieldNewConstructor.py b/python/testData/quickFixes/AddFieldQuickFixTest/addFieldNewConstructor.py similarity index 100% rename from python/testData/inspections/AddFieldNewConstructor.py rename to python/testData/quickFixes/AddFieldQuickFixTest/addFieldNewConstructor.py diff --git a/python/testData/inspections/AddFieldNewConstructor_after.py b/python/testData/quickFixes/AddFieldQuickFixTest/addFieldNewConstructor_after.py similarity index 100% rename from python/testData/inspections/AddFieldNewConstructor_after.py rename to python/testData/quickFixes/AddFieldQuickFixTest/addFieldNewConstructor_after.py diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py index da65a030ea4d..040de7386118 100644 --- a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py @@ -3,8 +3,8 @@ __author__ = 'ktisha' class A: def __init__(self): - self._a = 1 self.b = 1 + self._a = 1 def foo(self): pass diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py index 2dc80743d949..9a83f676bfdc 100644 --- a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py @@ -3,8 +3,8 @@ __author__ = 'ktisha' class A: def __init__(self): - self._a = 1 self.b = 1 + self._a = 1 def foo(self): c = 1 \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index 70eeef9216ca..2e67d8a67592 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -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); diff --git a/python/testSrc/com/jetbrains/python/quickFixes/AddFieldQuickFixTest.java b/python/testSrc/com/jetbrains/python/quickFixes/AddFieldQuickFixTest.java new file mode 100644 index 000000000000..a337e38e10d5 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/quickFixes/AddFieldQuickFixTest.java @@ -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")); + } + +} From 036752af3ce6f8a1ec6ce33d5c51b0bf6a71dbcc Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Mon, 25 Mar 2013 15:34:34 +0400 Subject: [PATCH 2/8] fixed PY-9266 Running doctest for __test__ in module from PyCharm returns "TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'" --- python/helpers/pycharm/docrunner.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/helpers/pycharm/docrunner.py b/python/helpers/pycharm/docrunner.py index 37c4e3459e5f..2052a0729ece 100644 --- a/python/helpers/pycharm/docrunner.py +++ b/python/helpers/pycharm/docrunner.py @@ -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) From 24cef7726b083ac35e07f46d2052f6c324ec518c Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Mon, 25 Mar 2013 15:46:31 +0400 Subject: [PATCH 3/8] fixed PY-9264 Unify naming for inspections related to class constructors --- python/src/com/jetbrains/python/PyBundle.properties | 5 +++-- .../python/inspections/PyMissingConstructorInspection.java | 4 ++-- python/testData/inspections/AddCallSuper.py | 2 +- python/testData/inspections/AddCallSuper1.py | 2 +- python/testData/inspections/AddCallSuperPass.py | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index c224e40e0673..8fc2359087fd 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -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 {0}
to accommodate new field {1} +QFIX.added.constructor.$0.for.field.$1=Added a __init__ to class {0}
to accommodate new field {1} QFIX.failed.to.add.field=
Failed to add a field!

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 diff --git a/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java b/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java index c19730ba8f35..6f3d6d119e30 100644 --- a/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyMissingConstructorInspection.java @@ -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")); } } diff --git a/python/testData/inspections/AddCallSuper.py b/python/testData/inspections/AddCallSuper.py index 40590dbc812e..bc63275f6ca3 100644 --- a/python/testData/inspections/AddCallSuper.py +++ b/python/testData/inspections/AddCallSuper.py @@ -3,5 +3,5 @@ class A: pass class B(A): - def __init__(self, r, b = 6, *args, **kwargs): + def __init__(self, r, b = 6, *args, **kwargs): print "Constructor B was called" diff --git a/python/testData/inspections/AddCallSuper1.py b/python/testData/inspections/AddCallSuper1.py index 2955eb77fe08..3085b5db9e63 100644 --- a/python/testData/inspections/AddCallSuper1.py +++ b/python/testData/inspections/AddCallSuper1.py @@ -3,6 +3,6 @@ class A: pass class B(A): - def __init__(self, r, b = 6): + def __init__(self, r, b = 6): """docstring""" print "Constructor B was called" diff --git a/python/testData/inspections/AddCallSuperPass.py b/python/testData/inspections/AddCallSuperPass.py index 2cf98244251f..9bc68003fc21 100644 --- a/python/testData/inspections/AddCallSuperPass.py +++ b/python/testData/inspections/AddCallSuperPass.py @@ -3,5 +3,5 @@ class A: pass class B(A): - def __init__(self, r, b = 6): + def __init__(self, r, b = 6): pass From 097ed48b0ae30c8b08413732bd914315d2dc37ca Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Mon, 25 Mar 2013 15:58:12 +0400 Subject: [PATCH 4/8] fixed PY-9263 Move attribute to init method: add super class call when moving to not yet existing init reused AddFieldQuickFix logic --- .../quickfix/AddFieldQuickFix.java | 24 +++++++++-------- .../PyMoveAttributeToInitQuickFix.java | 27 +++---------------- .../addPass_after.py | 2 +- .../addSuperCall.py | 9 +++++++ .../addSuperCall_after.py | 13 +++++++++ .../moveToInit_after.py | 2 +- .../PyMoveAttributeToInitQuickFixTest.java | 4 +++ 7 files changed, 45 insertions(+), 36 deletions(-) create mode 100644 python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addSuperCall.py create mode 100644 python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addSuperCall_after.py diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java index 44e1e05fe308..de8185679e81 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddFieldQuickFix.java @@ -98,8 +98,8 @@ public class AddFieldQuickFix implements LocalQuickFix { } @Nullable - public static PsiElement addFieldToInit(Project project, PyClass cls, String item_name, Function callback) { - if (cls != null && item_name != null) { + public static PsiElement addFieldToInit(Project project, PyClass cls, String itemName, Function callback) { + if (cls != null && itemName != null) { PyFunction init = cls.findMethodByName(PyNames.INIT, false); if (init != null) { return appendToMethod(init, callback); @@ -109,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 } } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyMoveAttributeToInitQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyMoveAttributeToInitQuickFix.java index 5eae3b68ab2e..022dd853588d 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyMoveAttributeToInitQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyMoveAttributeToInitQuickFix.java @@ -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 callback = FunctionUtil.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, true); - return true; - } - - final PyStatementList statementList = init.getStatementList(); - if (statementList == null) return false; - PyUtil.addElementToStatementList(copy, statementList, true); - return true; - } - private static boolean removeDefinition(PyAssignmentStatement assignment) { final PyStatementList statementList = PsiTreeUtil.getParentOfType(assignment, PyStatementList.class); if (statementList == null) return false; diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py index 040de7386118..da65a030ea4d 100644 --- a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py @@ -3,8 +3,8 @@ __author__ = 'ktisha' class A: def __init__(self): - self.b = 1 self._a = 1 + self.b = 1 def foo(self): pass diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addSuperCall.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addSuperCall.py new file mode 100644 index 000000000000..5cfb8c150ead --- /dev/null +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addSuperCall.py @@ -0,0 +1,9 @@ +__author__ = 'ktisha' + +class Base(object): + def __init__(self): + self.param = 2 + +class Child(Base): + def f(self): + self.my = 2 \ No newline at end of file diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addSuperCall_after.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addSuperCall_after.py new file mode 100644 index 000000000000..4dc2613e8ca4 --- /dev/null +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addSuperCall_after.py @@ -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 diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py index 9a83f676bfdc..2dc80743d949 100644 --- a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py @@ -3,8 +3,8 @@ __author__ = 'ktisha' class A: def __init__(self): - self.b = 1 self._a = 1 + self.b = 1 def foo(self): c = 1 \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/quickFixes/PyMoveAttributeToInitQuickFixTest.java b/python/testSrc/com/jetbrains/python/quickFixes/PyMoveAttributeToInitQuickFixTest.java index b02d5fd4f092..4a4bce54a4b6 100644 --- a/python/testSrc/com/jetbrains/python/quickFixes/PyMoveAttributeToInitQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/quickFixes/PyMoveAttributeToInitQuickFixTest.java @@ -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")); + } + } From b6c860fbaf08466c9e5447627b41eacffe700995 Mon Sep 17 00:00:00 2001 From: Ekaterina Tuzova Date: Mon, 25 Mar 2013 16:30:45 +0400 Subject: [PATCH 5/8] fixed PY-9262 Instance attribute defined outside init: false positive with superclass constructor call in class --- .../PyAttributeOutsideInitInspection.java | 32 ++++++++++++++++--- .../definedInSuperClass.py | 12 +++++++ .../PyAttributeOutsideInitInspectionTest.java | 4 +++ 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 python/testData/inspections/PyAttributeOutsideInitInspection/definedInSuperClass.py diff --git a/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java b/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java index b6add93f047c..b4ef1ff0e27c 100644 --- a/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java @@ -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 attributesInInit = new HashMap(); final PyFunction initMethod = containingClass.findMethodByName(PyNames.INIT, false); - if (initMethod != null) + if (initMethod != null) { PyClassImpl.collectInstanceAttributes(initMethod, attributesInInit); + collectAttributesFromSuper(attributesInInit, initMethod); + } + Map attributes = new HashMap(); PyClassImpl.collectInstanceAttributes(node, attributes); @@ -66,5 +70,25 @@ public class PyAttributeOutsideInitInspection extends PyInspection { } } + private void collectAttributesFromSuper(Map 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); + } + } + } + } + } + } + } } } diff --git a/python/testData/inspections/PyAttributeOutsideInitInspection/definedInSuperClass.py b/python/testData/inspections/PyAttributeOutsideInitInspection/definedInSuperClass.py new file mode 100644 index 000000000000..071e8096a381 --- /dev/null +++ b/python/testData/inspections/PyAttributeOutsideInitInspection/definedInSuperClass.py @@ -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 \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyAttributeOutsideInitInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyAttributeOutsideInitInspectionTest.java index 45bddc738a5c..dee5a034ec94 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyAttributeOutsideInitInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyAttributeOutsideInitInspectionTest.java @@ -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); From cfdb655eeef70c12c027be77bc554a210c68e24e Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Mon, 25 Mar 2013 16:50:46 +0100 Subject: [PATCH 6/8] Fixed path evaluation. --- .../jetbrains/python/psi/impl/PyPathEvaluator.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/impl/PyPathEvaluator.java b/python/src/com/jetbrains/python/psi/impl/PyPathEvaluator.java index ce8e00216acb..559c20ba52d3 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyPathEvaluator.java +++ b/python/src/com/jetbrains/python/psi/impl/PyPathEvaluator.java @@ -98,18 +98,13 @@ public class PyPathEvaluator { } public static String evaluatePathInJoin(String containingFilePath, PyExpression[] args, int endElement, Set visited) { - String result = null; + String result = containingFilePath; for (int i = 0; i < endElement; i++) { - String arg = evaluate(args[i], containingFilePath, visited); + String arg = evaluate(args[i], result, visited); if (arg == null) { return null; } - if (result == null) { - result = arg; - } - else { - result = new File(result, arg).getPath(); - } + result = arg; } return result; } From 47c38ee8476afb631fa7dda0f842a05ff9edf5a1 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Mon, 25 Mar 2013 16:53:11 +0100 Subject: [PATCH 7/8] Fixed renaming file reference with defined context (PY-9147). --- .../psi/FileReferenceWithOneContext.java | 49 +++++++++++++++++++ .../python/psi/WeakFileReference.java | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 python/openapi/src/com/jetbrains/python/psi/FileReferenceWithOneContext.java diff --git a/python/openapi/src/com/jetbrains/python/psi/FileReferenceWithOneContext.java b/python/openapi/src/com/jetbrains/python/psi/FileReferenceWithOneContext.java new file mode 100644 index 000000000000..32fc7881f9e4 --- /dev/null +++ b/python/openapi/src/com/jetbrains/python/psi/FileReferenceWithOneContext.java @@ -0,0 +1,49 @@ +/* + * 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.vfs.VirtualFile; +import com.intellij.psi.PsiFileSystemItem; +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 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 getContextsForBindToElement(VirtualFile curVFile, Project project, FileReferenceHelper helper) { + return getContexts(); + } +} diff --git a/python/openapi/src/com/jetbrains/python/psi/WeakFileReference.java b/python/openapi/src/com/jetbrains/python/psi/WeakFileReference.java index 985835e36731..4fedebb9f0d1 100644 --- a/python/openapi/src/com/jetbrains/python/psi/WeakFileReference.java +++ b/python/openapi/src/com/jetbrains/python/psi/WeakFileReference.java @@ -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); } From 7380ea131c50f692633fe2167745e9dd55365b9f Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Mon, 25 Mar 2013 18:42:16 +0100 Subject: [PATCH 8/8] Fixed renaming of django file references (PY-9146). --- .../python/psi/FileReferenceWithOneContext.java | 15 +++++++++++++++ .../templateLanguages/TemplateFileReference.java | 2 +- .../python/psi/impl/PyPathEvaluator.java | 16 +++++++++++----- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/python/openapi/src/com/jetbrains/python/psi/FileReferenceWithOneContext.java b/python/openapi/src/com/jetbrains/python/psi/FileReferenceWithOneContext.java index 32fc7881f9e4..167db43a126b 100644 --- a/python/openapi/src/com/jetbrains/python/psi/FileReferenceWithOneContext.java +++ b/python/openapi/src/com/jetbrains/python/psi/FileReferenceWithOneContext.java @@ -17,11 +17,15 @@ 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; @@ -46,4 +50,15 @@ public class FileReferenceWithOneContext extends FileReference { protected Collection 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); + } + } } diff --git a/python/openapi/src/com/jetbrains/python/templateLanguages/TemplateFileReference.java b/python/openapi/src/com/jetbrains/python/templateLanguages/TemplateFileReference.java index 6dce0f0ad5b8..1a1c733f535b 100644 --- a/python/openapi/src/com/jetbrains/python/templateLanguages/TemplateFileReference.java +++ b/python/openapi/src/com/jetbrains/python/templateLanguages/TemplateFileReference.java @@ -82,7 +82,7 @@ public class TemplateFileReference extends WeakFileReference { dstItem = _dstItem; } - final Collection contexts = getFileReferenceSet().getDefaultContexts(); + final Collection contexts = getContexts(); switch (contexts.size()) { case 0: break; diff --git a/python/src/com/jetbrains/python/psi/impl/PyPathEvaluator.java b/python/src/com/jetbrains/python/psi/impl/PyPathEvaluator.java index 559c20ba52d3..1de88c7712d9 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyPathEvaluator.java +++ b/python/src/com/jetbrains/python/psi/impl/PyPathEvaluator.java @@ -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) { @@ -98,13 +99,18 @@ public class PyPathEvaluator { } public static String evaluatePathInJoin(String containingFilePath, PyExpression[] args, int endElement, Set visited) { - String result = containingFilePath; + String result = null; for (int i = 0; i < endElement; i++) { - String arg = evaluate(args[i], result, visited); + String arg = evaluate(args[i], containingFilePath, visited); if (arg == null) { return null; } - result = arg; + if (result == null) { + result = arg; + } + else { + result = new File(result, arg).getPath().replace("\\", "/"); + } } return result; }