diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/BuiltinAndSelfAttribute.py b/python/testData/inspections/PyUnboundLocalVariableInspection/BuiltinAndSelfAttribute.py new file mode 100644 index 000000000000..692aee71f830 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/BuiltinAndSelfAttribute.py @@ -0,0 +1,3 @@ +class C(): + def __init__(self): + self.str = str(1) #pass diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/ClassLevelSameName.py b/python/testData/inspections/PyUnboundLocalVariableInspection/ClassLevelSameName.py new file mode 100644 index 000000000000..51f0f8eee285 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/ClassLevelSameName.py @@ -0,0 +1,9 @@ +local_var = "foo" + +class C: + local_var = local_var #pass + + def foo(self): + print(self.local_var) + +C().foo() diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/ControlFlowInAssert.py b/python/testData/inspections/PyUnboundLocalVariableInspection/ControlFlowInAssert.py new file mode 100644 index 000000000000..b1cc192e89fb --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/ControlFlowInAssert.py @@ -0,0 +1,10 @@ +# PY-1138 +from models import ChartRequest +from components.dbutil import DbSession + +def foo(requestId): + with DbSession() as db: + req = db.query(ChartRequest).get(requestId) + assert req is not None, u"Invalid request" + print req + diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/ControlFlowInTryExceptFinally.py b/python/testData/inspections/PyUnboundLocalVariableInspection/ControlFlowInTryExceptFinally.py new file mode 100644 index 000000000000..40152671fb74 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/ControlFlowInTryExceptFinally.py @@ -0,0 +1,20 @@ +def foo1(): + a = 1 + try: + for i in range(10): + pass + except Exception: + pass + finally: + b = a #pass + + +def foo2(): + a = 1 + try: + for i in range(10): + pass + except Exception: + c = a #pass + finally: + b = a #pass diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/DecoratorAndParameter.py b/python/testData/inspections/PyUnboundLocalVariableInspection/DecoratorAndParameter.py new file mode 100644 index 000000000000..634add70d057 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/DecoratorAndParameter.py @@ -0,0 +1,7 @@ +def f(x): + def d(f): + return f + @d #pass + def g(d): + return d + return g(x) diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/DefaultArgument.py b/python/testData/inspections/PyUnboundLocalVariableInspection/DefaultArgument.py new file mode 100644 index 000000000000..a7843f01f1e1 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/DefaultArgument.py @@ -0,0 +1,5 @@ +def f(): + z = 2 + def g(z=z): #pass + return z + return g diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/DefinedInTryAndExcept.py b/python/testData/inspections/PyUnboundLocalVariableInspection/DefinedInTryAndExcept.py new file mode 100644 index 000000000000..02ba98741f5d --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/DefinedInTryAndExcept.py @@ -0,0 +1,8 @@ +def f(g): + try: + x = g() + except Exception: + x = g() + finally: + pass + print(x) #pass diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/DefinedInTryElse.py b/python/testData/inspections/PyUnboundLocalVariableInspection/DefinedInTryElse.py new file mode 100644 index 000000000000..f30273ba7515 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/DefinedInTryElse.py @@ -0,0 +1,9 @@ +def f(g): + x = 1 + try: + pass + except Exception: + pass + else: + x = g() + print(x) #pass diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/DefinedInTryUsedAfter.py b/python/testData/inspections/PyUnboundLocalVariableInspection/DefinedInTryUsedAfter.py new file mode 100644 index 000000000000..415aef387c27 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/DefinedInTryUsedAfter.py @@ -0,0 +1,6 @@ +def f(): + try: + result = bool(2) + finally: + pass + return result #pass diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/ForBreakElse.py b/python/testData/inspections/PyUnboundLocalVariableInspection/ForBreakElse.py new file mode 100644 index 000000000000..d78ceb9499f4 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/ForBreakElse.py @@ -0,0 +1,8 @@ +def f(c): + for i in [1, 2, 3]: + if c: + x = 0 + break + else: + x = 1 + return x #pass diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/ImplicitIfNotNone.py b/python/testData/inspections/PyUnboundLocalVariableInspection/ImplicitIfNotNone.py new file mode 100644 index 000000000000..117517b7b10b --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/ImplicitIfNotNone.py @@ -0,0 +1,6 @@ +foo = True + + +def bar(): + if foo: #pass + print 1 diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/InstanceAttributeOutsideClass.py b/python/testData/inspections/PyUnboundLocalVariableInspection/InstanceAttributeOutsideClass.py new file mode 100644 index 000000000000..5fc2a210625a --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/InstanceAttributeOutsideClass.py @@ -0,0 +1,11 @@ +class C(object): + pass + + +class D(object): + def f(self): + def g(x): + x = C() + x.y = 1 #pass + return x + return g diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/InstanceFieldAndGlobal.py b/python/testData/inspections/PyUnboundLocalVariableInspection/InstanceFieldAndGlobal.py new file mode 100644 index 000000000000..e4122018fc1e --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/InstanceFieldAndGlobal.py @@ -0,0 +1,5 @@ +x = 1 + +class C: + def __init__(self): + self.x = x #pass diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/LocalFunctionAndVariable.py b/python/testData/inspections/PyUnboundLocalVariableInspection/LocalFunctionAndVariable.py new file mode 100644 index 000000000000..8c38281c6710 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/LocalFunctionAndVariable.py @@ -0,0 +1,4 @@ +def x(arg): + def foo(): pass + if arg: foo = None + callee(foo) #pass diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/OuterFunctionsAndSelfAttributes.py b/python/testData/inspections/PyUnboundLocalVariableInspection/OuterFunctionsAndSelfAttributes.py new file mode 100644 index 000000000000..d2cd736c4d30 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/OuterFunctionsAndSelfAttributes.py @@ -0,0 +1,13 @@ +def g(): + pass + + +class C: + pass + + +class D: + def m(self): + self.g = g() #pass + self.C = C() #pass + diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/ParamAfterTryExcept.py b/python/testData/inspections/PyUnboundLocalVariableInspection/ParamAfterTryExcept.py new file mode 100644 index 000000000000..608548faa78b --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/ParamAfterTryExcept.py @@ -0,0 +1,6 @@ +def f(x): + try: + pass + except Exception: + pass + print(x) #pass diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/Simple.py b/python/testData/inspections/PyUnboundLocalVariableInspection/Simple.py new file mode 100644 index 000000000000..040d2335ac90 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/Simple.py @@ -0,0 +1,30 @@ +a = 1 + +def foo(): + print(a) #fail + a = 21 + print(a) #pass + + +foo() +print(a) + +if bla_bla(): #pass + b = 1 +print(b) #fail +print(b) #pass +c = 1 + +def buzz(): + print(c) #pass + +buzz() + +print BLA_BLA_BLA #pass + +if foo(): + d = 1 +else: + d = 2 + +print(d) #pass diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/StarImportTopLevel.py b/python/testData/inspections/PyUnboundLocalVariableInspection/StarImportTopLevel.py new file mode 100644 index 000000000000..2def04cfae38 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/StarImportTopLevel.py @@ -0,0 +1,2 @@ +from re import * +print(UNICODE) diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/SysExit.py b/python/testData/inspections/PyUnboundLocalVariableInspection/SysExit.py new file mode 100644 index 000000000000..36513e13a8b1 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/SysExit.py @@ -0,0 +1,3 @@ +def f(): + import sys + sys.exit(1) #pass diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundAugmentedAssignment.py b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundAugmentedAssignment.py new file mode 100644 index 000000000000..539cc26f7ee6 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundAugmentedAssignment.py @@ -0,0 +1,5 @@ +def f(c): + if c: + x = 1 + x += 1 #fail + return x diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundConditionalImport.py b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundConditionalImport.py new file mode 100644 index 000000000000..978e0861ab60 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundConditionalImport.py @@ -0,0 +1,4 @@ +def f(c): + if c: + import sys + return sys diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundConditionalImportAlias.py b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundConditionalImportAlias.py new file mode 100644 index 000000000000..d867d1f49287 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundConditionalImportAlias.py @@ -0,0 +1,6 @@ +def f(c, x): + if c: + from re import compile as g + else: + pass + return g(x) #fail diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundConditionalImportQualified.py b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundConditionalImportQualified.py new file mode 100644 index 000000000000..1e40687e3dab --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundConditionalImportQualified.py @@ -0,0 +1,6 @@ +def f(c, x): + if c: + import os.path + else: + pass + return os.path.isfile(x) #fail diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundDefinedInTryUsedAfterExcept.py b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundDefinedInTryUsedAfterExcept.py new file mode 100644 index 000000000000..4afc68009bd3 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundDefinedInTryUsedAfterExcept.py @@ -0,0 +1,6 @@ +def a2(): + try: + a = 2 + except Exception: + pass + print(a) #fail diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundExceptAs.py b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundExceptAs.py new file mode 100644 index 000000000000..e9682e0b75b8 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundExceptAs.py @@ -0,0 +1,6 @@ +def f(): + try: + raise KeyError("ms") + except KeyError as e: + print(e) #pass + print(e) #fail diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundLoopVariable.py b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundLoopVariable.py new file mode 100644 index 000000000000..c3bc9d70f3b5 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundLoopVariable.py @@ -0,0 +1,3 @@ +for i, x in enumerate([]): + print(i, x) +print(i) # fail diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundNestedComprehension.py b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundNestedComprehension.py new file mode 100644 index 000000000000..22f0a87065cb --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundNestedComprehension.py @@ -0,0 +1,4 @@ +def f(xs): + # vs is unbound + return [(k, v) for v in vs + for k, vs in xs.items()] diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundNonLocal.py b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundNonLocal.py new file mode 100644 index 000000000000..3d1307090707 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundNonLocal.py @@ -0,0 +1,24 @@ +def f1(): + nonlocal x #fail + + +def f2(): + def g(): + nonlocal x #fail + print(x) + + +x = 1 + +def f3(): + nonlocal x #fail + x = 2 + + +def f4(): + x = 0 + def g(): + nonlocal x #pass + x = 2 + return x + return g() diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundSwapStrUnicode.py b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundSwapStrUnicode.py new file mode 100644 index 000000000000..090eb744fbea --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundSwapStrUnicode.py @@ -0,0 +1,7 @@ +def f(): + bytes, str = str, unicode #fail + +class C(object): + bytes, str = str, unicode #pass + +bytes, str = str, unicode #pass diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundVariableFunctionCall.py b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundVariableFunctionCall.py new file mode 100644 index 000000000000..6a39f123b805 --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/UnboundVariableFunctionCall.py @@ -0,0 +1,7 @@ +def f(g, h, x): + if x > 1: + z = g + elif x: + z = h + # local z may be unbound, inspection fails only when z is in function call + return z() #fail diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/UseAfterWithAndRaise.py b/python/testData/inspections/PyUnboundLocalVariableInspection/UseAfterWithAndRaise.py new file mode 100644 index 000000000000..f49b37e01a5c --- /dev/null +++ b/python/testData/inspections/PyUnboundLocalVariableInspection/UseAfterWithAndRaise.py @@ -0,0 +1,7 @@ +def f(y, c, xs): + if c: + raise Exception() + for x in xs: + with y: + pass + print(x) #pass diff --git a/python/testData/inspections/PyUnboundLocalVariableInspection/expected.xml b/python/testData/inspections/PyUnboundLocalVariableInspection/expected.xml deleted file mode 100644 index 5aaae21724f3..000000000000 --- a/python/testData/inspections/PyUnboundLocalVariableInspection/expected.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - test.py - 4 - "Local variable 'a' might be referenced before assignment" - - - test.py - 14 - "Name 'b' can be not defined" - - - test.py - 51 - "Name 'i' can be not defined" - - - test.py - 77 - "Local variable 'z' might be referenced before assignment" - - - test.py - 83 - "Local variable 'vs' might be referenced before assignment" - - - test.py - 123 - "Local variable 'sys' might be referenced before assignment" - - - test.py - 132 - "Local variable 'g' might be referenced before assignment" - - - test.py - 141 - "Local variable 'os' might be referenced before assignment" - - - test.py - 148 - "Local variable 'x' might be referenced before assignment" - - - test.py - 192 - "Local variable 'a' might be referenced before assignment" - - - test.py - 220 - "Local variable 'str' might be referenced before assignment" - - \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java b/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java index 78950a0aa6ab..7439dddfc5fa 100644 --- a/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java +++ b/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java @@ -94,6 +94,7 @@ public class PythonAllTestsSuite { PyCompatibilityInspectionTest.class, PyUnresolvedReferencesInspectionTest.class, PyCallingNonCallableInspectionTest.class, + PyUnboundLocalVariableInspectionTest.class, PyUnusedImportTest.class, PyDeprecationTest.class, PythonHighlightingLexerTest.class, diff --git a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java index 3d453c1b101e..0ed83ce7872c 100644 --- a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java +++ b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java @@ -129,16 +129,6 @@ public class PythonInspectionsTest extends PyTestCase { doTestWithLanguageLevel(getTestName(false), inspection, LanguageLevel.PYTHON24); } - public void testPyUnboundLocalVariableInspection() { - LocalInspectionTool inspection = new PyUnboundLocalVariableInspection(); - doTestWithLanguageLevel(getTestName(false), inspection, LanguageLevel.PYTHON27); - } - - public void testPyUnboundLocalVariableInspectionPy3k() { - LocalInspectionTool inspection = new PyUnboundLocalVariableInspection(); - doTestWithPy3k(getTestName(false), inspection); - } - public void testPyDocstringInspection() { LocalInspectionTool inspection = new PyDocstringInspection(); doTest(getTestName(false), inspection); diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnboundLocalVariableInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnboundLocalVariableInspectionTest.java new file mode 100644 index 000000000000..b46fb6c655fa --- /dev/null +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnboundLocalVariableInspectionTest.java @@ -0,0 +1,181 @@ +package com.jetbrains.python.inspections; + +import com.jetbrains.python.fixtures.PyTestCase; +import com.jetbrains.python.psi.LanguageLevel; + +/** + * @author vlan + */ +public class PyUnboundLocalVariableInspectionTest extends PyTestCase { + private static final String TEST_DIRECTORY = "inspections/PyUnboundLocalVariableInspection/"; + + public void testSimple() { + doTest(); + } + + // PY-1138 + public void testControlFlowInAssert() { + doTest(); + } + + // PY-1176 + public void testLocalFunctionAndVariable() { + doTest(); + } + + // PY-1359 + public void testUnboundLoopVariable() { + doTest(); + } + + // PY-1408 + public void testUnboundExceptAs() { + runWithLanguageLevel(LanguageLevel.PYTHON33, new Runnable() { + @Override + public void run() { + doTest(); + } + }); + } + + // PY-1434 + public void testClassLevelSameName() { + doTest(); + } + + // PY-1435 + public void testInstanceFieldAndGlobal() { + doTest(); + } + + // PY-3343 + public void testUnboundVariableFunctionCall() { + doTest(); + } + + // PY-3407 + public void testUnboundNestedComprehension() { + doTest(); + } + + // PY-3503 + public void testControlFlowInTryExceptFinally() { + doTest(); + } + + // PY-3550 + public void testDefaultArgument() { + doTest(); + } + + // PY-3583 + public void testUnboundConditionalImport() { + doTest(); + } + + // PY-3603 + public void testUnboundNonLocal() { + runWithLanguageLevel(LanguageLevel.PYTHON33, new Runnable() { + @Override + public void run() { + doTest(); + } + }); + } + + // PY-3671 + public void testUnboundConditionalImportAlias() { + doTest(); + } + + // PY-3665 + public void testUnboundConditionalImportQualified() { + doTest(); + } + + // PY-3651 + public void testUnboundAugmentedAssignment() { + doTest(); + } + + // PY-3702 + public void testSysExit() { + doTest(); + } + + // PY-3895 + public void testDecoratorAndParameter() { + doTest(); + } + + // PY-4102 + public void testDefinedInTryUsedAfter() { + doTest(); + } + + // PY-4150 + public void testParamAfterTryExcept() { + doTest(); + } + + // PY-4151 + public void testUnboundDefinedInTryUsedAfterExcept() { + doTest(); + } + + // PY-4152 + public void testDefinedInTryAndExcept() { + doTest(); + } + + // PY-4157 + public void testDefinedInTryElse() { + doTest(); + } + + // PY-4197 + public void testUnboundSwapStrUnicode() { + doTest(); + } + + // PY-4229 + public void testInstanceAttributeOutsideClass() { + doTest(); + } + + // PY-4297 + public void testOuterFunctionsAndSelfAttributes() { + doTest(); + } + + // PY-4623 + public void testBuiltinAndSelfAttribute() { + doTest(); + } + + // PY-4609 + public void testImplicitIfNotNone() { + doTest(); + } + + // PY-4239 + public void testForBreakElse() { + doTest(); + } + + // PY-5592 + public void testStarImportTopLevel() { + doTest(); + } + + // PY-7966 + public void testUseAfterWithAndRaise() { + doTest(); + } + + private void doTest() { + myFixture.configureByFile(TEST_DIRECTORY + getTestName(false) + ".py"); + myFixture.enableInspections(PyUnboundLocalVariableInspection.class); + myFixture.checkHighlighting(true, false, false); + } +}