Repository: pylint-dev/astroid

Issue:
infer_stmts cannot infer multiple uses of the same AssignName
Given multiple assignments to the same target which both reference the same AssignName, infer_stmts fails for subsequent attempts after the first.

### Steps to reproduce

This appears to be a minimum working example, removing any part removes the effect:

```python
fails = astroid.extract_node("""
    pair = [1, 2]
    ex = pair[0]
    if 1 + 1 == 2:
        ex = pair[1]
    ex
""")
print(list(fails.infer()))
# [<Const.int l.2 at 0x...>, Uninferable]
```

For some context, I originally saw this with attributes on an imported module, i.e.

```python
import mod
ex = mod.One()
# later ... or in some branch
ex = mod.Two()
```

### Current behavior

See above.

### Expected behavior

Inlining the variable or switching to a different name works fine:

```python
works = astroid.extract_node("""
    # pair = [1, 2]
    ex = [1, 2][0]
    if 1 + 1 == 2:
        ex = [1, 2][1]
    ex
""")
print(list(works.infer()))
# [<Const.int l.3 at 0x...>, <Const.int l.5 at 0x...>]

works = astroid.extract_node("""
    first = [1, 2]
    second = [1, 2]
    ex = first[0]
    if 1 + 1 == 2:
        ex = second[1]
    ex
""")
print(list(works.infer()))
# [<Const.int l.2 at 0x...>, <Const.int l.3 at 0x...>]
```

I would expect that the first failing example would work similarly. This (only) worked
in astroid 2.5 and appears to have been "broken" by the revert of cc3bfc5 in 03d15b0 (astroid 2.5.1 and above).

### ``python -c "from astroid import __pkginfo__; print(__pkginfo__.version)"`` output

```
$ python -c "from astroid import __pkginfo__; print(__pkginfo__.version)"
2.5-dev
$ git rev-parse HEAD
03d15b0f32f7d7c9b2cb062b9321e531bd954344
```



Discussion / hints:

Fix (patch):
diff --git a/ChangeLog b/ChangeLog
index 12fae5ceab..05d9256b7e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -14,6 +14,11 @@ Release Date: TBA
  Closes PyCQA/pylint#3970
  Closes PyCQA/pylint#3595
 
+* Fix some spurious cycles detected in ``context.path`` leading to more cases
+  that can now be inferred
+
+  Closes #926
+
 
 What's New in astroid 2.5.6?
 ============================
diff --git a/astroid/context.py b/astroid/context.py
index 18220ec228..d7bf81bf17 100644
--- a/astroid/context.py
+++ b/astroid/context.py
@@ -102,7 +102,7 @@ def clone(self):
         starts with the same context but diverge as each side is inferred
         so the InferenceContext will need be cloned"""
         # XXX copy lookupname/callcontext ?
-        clone = InferenceContext(self.path, inferred=self.inferred)
+        clone = InferenceContext(self.path.copy(), inferred=self.inferred.copy())
         clone.callcontext = self.callcontext
         clone.boundnode = self.boundnode
         clone.extra_context = self.extra_context


Test changes:
diff --git a/tests/unittest_brain_numpy_core_umath.py b/tests/unittest_brain_numpy_core_umath.py
index 12c1e3bbbc..5559bdd581 100644
--- a/tests/unittest_brain_numpy_core_umath.py
+++ b/tests/unittest_brain_numpy_core_umath.py
@@ -14,7 +14,7 @@
 except ImportError:
     HAS_NUMPY = False
 
-from astroid import bases, builder, nodes, util
+from astroid import bases, builder, nodes
 
 
 @unittest.skipUnless(HAS_NUMPY, "This test requires the numpy library.")
@@ -220,9 +220,7 @@ def test_numpy_core_umath_functions_return_type(self):
             with self.subTest(typ=func_):
                 inferred_values = list(self._inferred_numpy_func_call(func_))
                 self.assertTrue(
-                    len(inferred_values) == 1
-                    or len(inferred_values) == 2
-                    and inferred_values[-1].pytype() is util.Uninferable,
+                    len(inferred_values) == 1,
                     msg="Too much inferred values ({}) for {:s}".format(
                         inferred_values[-1].pytype(), func_
                     ),
diff --git a/tests/unittest_inference.py b/tests/unittest_inference.py
index ce15e1efd1..ba1637ceb3 100644
--- a/tests/unittest_inference.py
+++ b/tests/unittest_inference.py
@@ -1704,7 +1704,8 @@ def __init__(self):
         """
         ast = extract_node(code, __name__)
         expr = ast.func.expr
-        self.assertIs(next(expr.infer()), util.Uninferable)
+        with pytest.raises(exceptions.InferenceError):
+            next(expr.infer())
 
     def test_tuple_builtin_inference(self):
         code = """
@@ -6032,5 +6033,37 @@ def test_infer_list_of_uninferables_does_not_crash():
     assert not inferred.elts
 
 
+# https://github.com/PyCQA/astroid/issues/926
+def test_issue926_infer_stmts_referencing_same_name_is_not_uninferable():
+    code = """
+    pair = [1, 2]
+    ex = pair[0]
+    if 1 + 1 == 2:
+        ex = pair[1]
+    ex
+    """
+    node = extract_node(code)
+    inferred = list(node.infer())
+    assert len(inferred) == 2
+    assert isinstance(inferred[0], nodes.Const)
+    assert inferred[0].value == 1
+    assert isinstance(inferred[1], nodes.Const)
+    assert inferred[1].value == 2
+
+
+# https://github.com/PyCQA/astroid/issues/926
+def test_issue926_binop_referencing_same_name_is_not_uninferable():
+    code = """
+    pair = [1, 2]
+    ex = pair[0] + pair[1]
+    ex
+    """
+    node = extract_node(code)
+    inferred = list(node.infer())
+    assert len(inferred) == 1
+    assert isinstance(inferred[0], nodes.Const)
+    assert inferred[0].value == 3
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/tests/unittest_regrtest.py b/tests/unittest_regrtest.py
index 29febfb8f6..acabde135e 100644
--- a/tests/unittest_regrtest.py
+++ b/tests/unittest_regrtest.py
@@ -99,7 +99,7 @@ def test_numpy_crash(self):
         astroid = builder.string_build(data, __name__, __file__)
         callfunc = astroid.body[1].value.func
         inferred = callfunc.inferred()
-        self.assertEqual(len(inferred), 2)
+        self.assertEqual(len(inferred), 1)
 
     def test_nameconstant(self):
         # used to fail for Python 3.4