mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-17 05:07:37 +08:00
fix(resolution): explicit-alias check matches the exact module, outside strings
Review follow-up: `import other.pkg.settings as settings` or a string `"import pkg.settings as settings"` no longer marks a plain `import pkg.settings` as explicitly aliased, which attached a write to the wrong module. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018CQubRS1bXvaoApgPyLf66
This commit is contained in:
@@ -1439,6 +1439,16 @@ def init():
|
||||
namespace_import_binds_root: [{ 'pkg/__init__.py': '', 'pkg/settings.py': header,
|
||||
'c.py': 'import pkg.settings\nfrom decoy import Decoy\n\nsettings = Bag()\nsettings.conn = Decoy()\n',
|
||||
'd.py': 'from pkg import settings\n\ndef cb(pool):\n pool.submit(settings.conn.fetch)\n' }, ['cb -> Store::fetch']],
|
||||
// `other.pkg.settings as settings` is not an explicit alias of `pkg.settings`: the
|
||||
// write goes to the other module, so pkg's global keeps its type (the consumer's
|
||||
// relative import names pkg/settings.py exactly).
|
||||
alias_of_longer_module_is_not_explicit: [{ 'pkg/__init__.py': '', 'pkg/settings.py': header,
|
||||
'other/__init__.py': '', 'other/pkg/__init__.py': '', 'other/pkg/settings.py': 'conn = None\n',
|
||||
'reset.py': 'import pkg.settings\nimport other.pkg.settings as settings\nfrom decoy import Decoy\n\ndef reset():\n settings.conn = Decoy()\n',
|
||||
'pkg/d.py': 'from . import settings\n\ndef cb(pool):\n pool.submit(settings.conn.fetch)\n' }, ['cb -> Store::fetch']],
|
||||
alias_in_string_is_not_explicit: [{ 'pkg/__init__.py': '', 'pkg/settings.py': 'conn = None\n',
|
||||
'reset.py': 'import pkg.settings\nfrom decoy import Decoy\nnote = "import pkg.settings as settings"\n\ndef reset():\n settings.conn = Decoy()\n',
|
||||
'd.py': 'from pkg import settings\n\ndef cb(pool):\n pool.submit(settings.conn.fetch)\n' }, []],
|
||||
explicit_alias_equal_to_leaf: [{ 'pkg/__init__.py': '', 'pkg/settings.py': header,
|
||||
'reset.py': 'import pkg.settings as settings\nfrom decoy import Decoy\n\ndef reset():\n settings.conn = Decoy()\n',
|
||||
'd.py': 'from pkg import settings\n\ndef cb(pool):\n pool.submit(settings.conn.fetch)\n' }, []],
|
||||
|
||||
@@ -685,9 +685,10 @@ function pythonModuleAliases(filePath: string, moduleFile: string, context: Reso
|
||||
const files = pythonModuleFiles(dotted, filePath, context);
|
||||
if (!files.includes(moduleFile)) continue;
|
||||
// The mapping cannot tell `import a.b` from `import a.b as b`; the source line can.
|
||||
const explicit = new RegExp(`\\bimport\\s[^\\n]*\\b${m.source.replace(/\./g, '\\.')}\\s+as\\s+${m.localName}\\b`);
|
||||
// Exactly this module (not `other.a.b`), outside string literals.
|
||||
const explicit = new RegExp(`\\bimport\\s[^\\n]*(?<![\\w.])${m.source.replace(/\./g, '\\.')}\\s+as\\s+${m.localName}\\b`);
|
||||
const plainDotted = m.isNamespace && m.source.includes('.') && m.localName === m.source.split('.').pop() &&
|
||||
!pythonMemberLines(filePath, context).some(line => explicit.test(line));
|
||||
!pythonMemberLines(filePath, context).some(line => explicit.test(blankPythonStrings(line)));
|
||||
(files.length === 1 ? aliases : ambiguous).add(plainDotted ? m.source : m.localName);
|
||||
}
|
||||
return { aliases: [...aliases], ambiguous: [...ambiguous] };
|
||||
|
||||
Reference in New Issue
Block a user