Minor bug fixes:
- Fix error to resolve relative import in the current directory when there is no `__init__.py` file.
- Fix error on trying to read an empty file.
- Fix error where the import clobbering check failed on method names matching imported name (false positive).
- Fix error where rattr did not inspect the `elt` and `generators` of a `GeneratorExpr`.
Refactor:
- Move to `attrs` for internal data structures. This will allow for quicker serialisation and deserialisation via `cattrs` later if we wish.
Performance:
- Use a custom solution for resolving the source code file of an import, rather than using `importlib.util.find_spec`. This is because the latter is slow due to an (intended) side-effect in which the parent module is imported and thus module-level code is executed.
- Rework the plug-ins system to avoid `O(n^2)` code being repeated on each function analysed.
- Rework results simplification completely. Recursion (costly in Python) has been avoided and the tree is walked post-order.
- Shallow copy (not deep copy) target IR on creating a new `IrTarget` as we do not tamper with the value and this is much faster