Repository: pylint-dev/pylint

Issue:
pylint crashed with a ``AstroidError``
### Bug description

pylint crashed with a ``AstroidError``

### Configuration

```ini
[MESSAGES CONTROL]
disable=
    import-error,
    invalid-name,
    missing-module-docstring,
    missing-class-docstring,
    missing-function-docstring,
    too-few-public-methods,
    too-many-branches,
    too-many-locals,
```


### Command used

```shell
python -m pylint pytemplate.py
```


### Pylint output

```shell
************* Module pytemplate
pytemplate.py:5:0: C0103: Class name "__metaclass__" doesn't conform to PascalCase naming style (invalid-name)
pytemplate.py:13:0: C0115: Missing class docstring (missing-class-docstring)
pytemplate.py:18:4: C0116: Missing function or method docstring (missing-function-docstring)
pytemplate.py:23:4: C0116: Missing function or method docstring (missing-function-docstring)
Exception on node <Name.__module__ l.26 at 0x7f7fe5af1330> in file '/home/knoack/git-projects/tbs/ansible_collections/controlware/tbs/utils/pytemplate.py'
Traceback (most recent call last):
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/utils/ast_walker.py", line 91, in walk
    callback(astroid)
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/checkers/variables.py", line 1573, in visit_name
    self._undefined_and_used_before_checker(node, stmt)
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/checkers/variables.py", line 1614, in _undefined_and_used_before_checker
    action, nodes_to_consume = self._check_consumer(
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/checkers/variables.py", line 1772, in _check_consumer
    ) = self._is_variable_violation(
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/checkers/variables.py", line 2129, in _is_variable_violation
    and defframe.locals[node.name][0].lineno < frame.lineno
TypeError: '<' not supported between instances of 'NoneType' and 'int'
pytemplate.py:1:0: F0002: pytemplate.py: Fatal error while checking 'pytemplate.py'. Please open an issue in our bug tracker so we address this. There is a pre-filled template that you can use in '/home/knoack/.cache/pylint/pylint-crash-2023-07-20-13-04-04.txt'. (astroid-error)

------------------------------------------------------------------
Your code has been rated at 0.00/10 (previous run: 0.00/10, +0.00)
```


### Expected behavior

No exception

### Pylint version

```shell
python -m pylint --version
pylint 2.17.4
astroid 2.15.6
Python 3.10.6 (main, May 29 2023, 11:10:38) [GCC 11.3.0]
```


### OS / Environment

cat /etc/os-release 
PRETTY_NAME="Ubuntu 22.04.2 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.2 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

### Additional dependencies

cat /home/knoack/.cache/pylint/pylint-crash-2023-07-20-13-04-04.txt
First, please verify that the bug is not already filled:
https://github.com/PyCQA/pylint/issues/

Then create a new crash issue:
https://github.com/PyCQA/pylint/issues/new?assignees=&labels=crash%2Cneeds+triage&template=BUG-REPORT.yml


Issue title:
Crash ```` (if possible, be more specific about what made pylint crash)
Content:
When parsing the following file:

<!--
 If sharing the code is not an option, please state so,
 but providing only the stacktrace would still be helpful.
 -->

```python
"""Class to run and handle custom template using python code"""

from __future__ import absolute_import, division, print_function

__metaclass__ = type

import os
import sys
import importlib.util
from ansible.plugins.action import AnsibleActionFail


class PyTemplate:
    def __init__(self) -> None:
        self.loaded_modules = {}
        self.template_path = ""

    def run_template(self, template_vars: any) -> str:
        if template_vars is not None:
            return ""
        return ""

    def set_template_path(self, path: str) -> None:
        self.template_path = path

    def load_module_from_file(self, name: str, path: str) -> __module__:
        """
        name: system wide name of module (sys["modules"])
        path: relative path to pyhton code file (with or without .py ending)
        """
        if name in self.loaded_modules:
            return self.loaded_modules[name]

        py_ending = ".py"
        stripped_path = path.rstrip(py_ending)
        module_path = os.path.join(self.template_path, stripped_path + py_ending)
        if not os.path.isfile(module_path):
            raise FileExistsError(f"Cannot find module '{module_path}'.")
        try:
            spec = importlib.util.spec_from_file_location(name, module_path)
            imported_module = importlib.util.module_from_spec(spec)
            sys.modules[name] = imported_module
            self.loaded_modules[name] = imported_module
            spec.loader.exec_module(imported_module)
        except ImportError as ie:
            raise AnsibleActionFail(
                f"Cannot import python template {module_path}."
            ) from ie
        return imported_module

```

pylint crashed with a ``AstroidError`` and with the following stacktrace:
```
Traceback (most recent call last):
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/lint/pylinter.py", line 811, in _lint_file
    check_astroid_module(module)
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/lint/pylinter.py", line 1085, in check_astroid_module
    retval = self._check_astroid_module(
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/lint/pylinter.py", line 1135, in _check_astroid_module
    walker.walk(node)
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/utils/ast_walker.py", line 94, in walk
    self.walk(child)
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/utils/ast_walker.py", line 94, in walk
    self.walk(child)
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/utils/ast_walker.py", line 94, in walk
    self.walk(child)
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/utils/ast_walker.py", line 91, in walk
    callback(astroid)
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/checkers/variables.py", line 1573, in visit_name
    self._undefined_and_used_before_checker(node, stmt)
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/checkers/variables.py", line 1614, in _undefined_and_used_before_checker
    action, nodes_to_consume = self._check_consumer(
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/checkers/variables.py", line 1772, in _check_consumer
    ) = self._is_variable_violation(
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/checkers/variables.py", line 2129, in _is_variable_violation
    and defframe.locals[node.name][0].lineno < frame.lineno
TypeError: '<' not supported between instances of 'NoneType' and 'int'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/lint/pylinter.py", line 775, in _lint_files
    self._lint_file(fileitem, module, check_astroid_module)
  File "/home/knoack/git-projects/tbs/.venv/lib/python3.10/site-packages/pylint/lint/pylinter.py", line 813, in _lint_file
    raise astroid.AstroidError from e
astroid.exceptions.AstroidError
```


Discussion:
Thanks for this @kornoa! The reproducer can be simplified to:
```python
class A:
    def say_hello(self) -> __module__:
        ...
```
At first glance, `defframe.locals[node.name][0]` is an astroid node representing the name of the module in which `PyTemplate` is defined and which doesn't have a lineno.
I got another NoneType in an Astroid callback, but with this code:

```python
from dataclasses import dataclass
from enum import Enum


@dataclass
class Status(Enum):
    OFF = 0
```

```
Exception on node <AssignName.OFF l.7 at 0x7f11f4f0c1d0> in file '/home/an/git/zivid-sdk/relay_control.py'
Traceback (most recent call last):
  File "venv/lib64/python3.11/site-packages/pylint/utils/ast_walker.py", line 91, in walk
    callback(astroid)
  File "venv/lib64/python3.11/site-packages/pylint/checkers/base/name_checker/checker.py", line 488, in visit_assignname
    if utils.is_enum_member(node) or utils.is_assign_name_annotated_with(
       ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "venv/lib64/python3.11/site-packages/pylint/checkers/utils.py", line 2284, in is_enum_member
    enum_member_objects = frame.locals.get("__members__")[0].items
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^
TypeError: 'NoneType' object is not subscriptable
```

I can reproduce it in a minimal venv from this requirements.txt:
```
#
# Generated by:
# echo pylint > requiremetns.in && pip-compile --strip-extras requirements.in
#
astroid==3.0.0
dill==0.3.7
isort==5.12.0
mccabe==0.7.0
platformdirs==3.11.0
pylint==3.0.0
tomlkit==0.12.1
```

From this, I can also reproduce the simplified reproducer above.
Thanks for checking old issues. That seems to be https://github.com/pylint-dev/pylint/issues/9100 which is resolved on `master`!



Patch:
diff --git a/doc/whatsnew/fragments/8866.bugfix b/doc/whatsnew/fragments/8866.bugfix
new file mode 100644
index 0000000000..2eba31dccb
--- /dev/null
+++ b/doc/whatsnew/fragments/8866.bugfix
@@ -0,0 +1,3 @@
+Fixed a crash when the lineno of a variable used as an annotation wasn't available for ``undefined-variable``.
+
+Closes #8866
diff --git a/pylint/checkers/variables.py b/pylint/checkers/variables.py
index 8447320b89..8e8b0e0bfb 100644
--- a/pylint/checkers/variables.py
+++ b/pylint/checkers/variables.py
@@ -2295,13 +2295,14 @@ def _is_variable_violation(
             # using a name defined earlier in the class containing the function.
             if node is frame.returns and defframe.parent_of(frame.returns):
                 annotation_return = True
-                if (
-                    frame.returns.name in defframe.locals
-                    and defframe.locals[node.name][0].lineno < frame.lineno
-                ):
-                    # Detect class assignments with a name defined earlier in the
-                    # class. In this case, no warning should be raised.
-                    maybe_before_assign = False
+                if frame.returns.name in defframe.locals:
+                    definition = defframe.locals[node.name][0]
+                    if definition.lineno is None or definition.lineno < frame.lineno:
+                        # Detect class assignments with a name defined earlier in the
+                        # class. In this case, no warning should be raised.
+                        maybe_before_assign = False
+                    else:
+                        maybe_before_assign = True
                 else:
                     maybe_before_assign = True
             if isinstance(node.parent, nodes.Arguments):


Test patch:
diff --git a/tests/functional/u/undefined/undefined_variable.py b/tests/functional/u/undefined/undefined_variable.py
index 194de114d3..19c134e77b 100644
--- a/tests/functional/u/undefined/undefined_variable.py
+++ b/tests/functional/u/undefined/undefined_variable.py
@@ -380,3 +380,7 @@ def y(self) -> RepeatedReturnAnnotations:  # [undefined-variable]
         pass
     def z(self) -> RepeatedReturnAnnotations:  # [undefined-variable]
         pass
+
+class A:
+    def say_hello(self) -> __module__:  # [undefined-variable]
+        ...
diff --git a/tests/functional/u/undefined/undefined_variable.txt b/tests/functional/u/undefined/undefined_variable.txt
index ea707c910a..c527c76d96 100644
--- a/tests/functional/u/undefined/undefined_variable.txt
+++ b/tests/functional/u/undefined/undefined_variable.txt
@@ -37,3 +37,4 @@ undefined-variable:365:10:365:20:global_var_mixed_assignment:Undefined variable
 undefined-variable:377:19:377:44:RepeatedReturnAnnotations.x:Undefined variable 'RepeatedReturnAnnotations':UNDEFINED
 undefined-variable:379:19:379:44:RepeatedReturnAnnotations.y:Undefined variable 'RepeatedReturnAnnotations':UNDEFINED
 undefined-variable:381:19:381:44:RepeatedReturnAnnotations.z:Undefined variable 'RepeatedReturnAnnotations':UNDEFINED
+undefined-variable:385:27:385:37:A.say_hello:Undefined variable '__module__':UNDEFINED