Repository: pylint-dev/pylint

Issue:
Crash on ``enumerate(x, int(y))``
### Bug description

When parsing the following file:
```python
for _, _ in enumerate(x, int(y)):
    ...
```

pylint crashed with a ``AstroidError`` and with the following stacktrace:
```
Traceback (most recent call last):
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/lint/pylinter.py", line 811, in _lint_file
    check_astroid_module(module)
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/lint/pylinter.py", line 1085, in check_astroid_module
    retval = self._check_astroid_module(
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/lint/pylinter.py", line 1135, in _check_astroid_module
    walker.walk(node)
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/utils/ast_walker.py", line 94, in walk
    self.walk(child)
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/utils/ast_walker.py", line 91, in walk
    callback(astroid)
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/checkers/refactoring/refactoring_checker.py", line 700, in visit_for
    self._check_unnecessary_list_index_lookup(node)
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/checkers/refactoring/refactoring_checker.py", line 2229, in _check_unnecessary_list_index_lookup
    has_start_arg, confidence = self._enumerate_with_start(node)
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/checkers/refactoring/refactoring_checker.py", line 2347, in _enumerate_with_start
    start_val, confidence = self._get_start_value(start_arg)
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/checkers/refactoring/refactoring_checker.py", line 2368, in _get_start_value
    start_val = inferred.value if inferred else None
                ^^^^^^^^^^^^^^
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/astroid/bases.py", line 140, in __getattr__
    return getattr(self._proxied, name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'ClassDef' object has no attribute 'value'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/lint/pylinter.py", line 775, in _lint_files
    self._lint_file(fileitem, module, check_astroid_module)
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/lint/pylinter.py", line 813, in _lint_file
    raise astroid.AstroidError from e
astroid.exceptions.AstroidError
```

### Command used

```shell
.venv/bin/pylint scratch2.py
```


### Pylint output

```python
************* Module scratch2
scratch2.py:1:0: C0114: Missing module docstring (missing-module-docstring)
Exception on node <For l.1 at 0x7f1d71faa450> in file '/tmp/pylint_crash/scratch2.py'
Traceback (most recent call last):
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/utils/ast_walker.py", line 91, in walk
    callback(astroid)
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/checkers/refactoring/refactoring_checker.py", line 700, in visit_for
    self._check_unnecessary_list_index_lookup(node)
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/checkers/refactoring/refactoring_checker.py", line 2229, in _check_unnecessary_list_index_lookup
    has_start_arg, confidence = self._enumerate_with_start(node)
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/checkers/refactoring/refactoring_checker.py", line 2347, in _enumerate_with_start
    start_val, confidence = self._get_start_value(start_arg)
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/pylint/checkers/refactoring/refactoring_checker.py", line 2368, in _get_start_value
    start_val = inferred.value if inferred else None
                ^^^^^^^^^^^^^^
  File "/tmp/pylint_crash/.venv/lib/python3.11/site-packages/astroid/bases.py", line 140, in __getattr__
    return getattr(self._proxied, name)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'ClassDef' object has no attribute 'value'
scratch2.py:1:0: F0002: scratch2.py: Fatal error while checking 'scratch2.py'. Please open an issue in our bug tracker so we address this.
```


### Expected behavior

no crash

### Pylint version

```shell
pylint 2.17.6
astroid 2.15.8
Python 3.11.5 (main, Sep 14 2023, 11:48:30) [GCC 9.4.0]
```


### OS / Environment

Ubuntu 20.04.6 LTS

### Additional dependencies

```python
astroid==2.15.8
dill==0.3.7
isort==5.12.0
lazy-object-proxy==1.9.0
mccabe==0.7.0
platformdirs==3.10.0
pylint==2.17.6
tomlkit==0.12.1
wrapt==1.15.0
```



Discussion:
This is a nice one. `y` is undefined, so `int(y)` is inferred to be an `int`. But that seems to be the class `int` rather than an instance (or some kind of mix-up like that). And the class has no `value` field, hence the error.

Issues like this and https://github.com/pylint-dev/pylint/issues/9074 would probably be caught by proper Astroid typing.



Patch:
diff --git a/doc/whatsnew/fragments/9078.bugfix b/doc/whatsnew/fragments/9078.bugfix
new file mode 100644
index 0000000000..2e48abc887
--- /dev/null
+++ b/doc/whatsnew/fragments/9078.bugfix
@@ -0,0 +1,4 @@
+Fixed a crash when the ``start`` value in an ``enumerate`` was non-constant and impossible to infer
+(like in``enumerate(apples, start=int(random_apple_index)``) for ``unnecessary-list-index-lookup``.
+
+Closes #9078
diff --git a/pylint/checkers/refactoring/refactoring_checker.py b/pylint/checkers/refactoring/refactoring_checker.py
index 9ffcecce12..a8bce74854 100644
--- a/pylint/checkers/refactoring/refactoring_checker.py
+++ b/pylint/checkers/refactoring/refactoring_checker.py
@@ -2454,7 +2454,9 @@ def _get_start_value(self, node: nodes.NodeNG) -> tuple[int | None, Confidence]:
             and isinstance(node.operand, (nodes.Attribute, nodes.Name))
         ):
             inferred = utils.safe_infer(node)
-            start_val = inferred.value if inferred else None
+            # inferred can be an astroid.base.Instance as in 'enumerate(x, int(y))' or
+            # not correctly inferred (None)
+            start_val = inferred.value if isinstance(inferred, nodes.Const) else None
             return start_val, INFERENCE
         if isinstance(node, nodes.UnaryOp):
             return node.operand.value, HIGH


Test patch:
diff --git a/tests/functional/u/unnecessary/unnecessary_list_index_lookup.py b/tests/functional/u/unnecessary/unnecessary_list_index_lookup.py
index 3201d5407d..b9f8b73251 100644
--- a/tests/functional/u/unnecessary/unnecessary_list_index_lookup.py
+++ b/tests/functional/u/unnecessary/unnecessary_list_index_lookup.py
@@ -156,3 +156,14 @@ def _get_extra_attrs(self, extra_columns):
 for idx, val in enumerate(my_list):
     if (val := 42) and my_list[idx] == 'b':
         print(1)
+
+def regression_9078(apples, cant_infer_this):
+    """Regression test for https://github.com/pylint-dev/pylint/issues/9078."""
+    for _, _ in enumerate(apples, int(cant_infer_this)):
+        ...
+
+def random_uninferrable_start(pears):
+    import random # pylint: disable=import-outside-toplevel
+
+    for _, _ in enumerate(pears, random.choice([5, 42])):
+        ...