Proposal: Add an aliases property to definitions and imported symbols

# file_a.py
def foo():
    pass
bar = foo

# file_b.py
from file_a import bar
bar()

Currently, our parser would give us Definition(name="foo", fqn="file_a.foo") and Reference(name="bar", fqn="file_a.bar"). But we won't know that file_a.bar == file_a.foo, and therefore won't resolve the reference to foo.

To handle this, we should use assignment tracing to get all the aliases for a definition when we first parse the file. E.g. for file_a.py, the parser would return:

Definition(name="foo", fqn="file_a.foo", aliases=[("bar", byte_range)])

That way we can match file_a.bar to file_a.foo by checking for matching aliases.

We should do the same for imported symbols, too:

from utils import foo
bar = foo

->

ImportedSymbol(name="foo", fqn="utils.foo", aliases=[("bar", byte_range)])