Skip to content

builder: skip foreign v.mod project dirs when resolving imports (fix #27151) - #27231

Merged
medvednikov merged 3 commits into
masterfrom
fix-27151
May 24, 2026
Merged

medvednikov merged 3 commits into
masterfrom
fix-27151

Conversation

@medvednikov

Copy link
Copy Markdown
Member

Summary

  • When V is invoked from inside another project (e.g. vlang/coreutils) that contains a sibling directory whose name matches a vlib module (like src/sync/), find_module_path would pick that user directory before trying vlib, and parsing then failed with bad module definition: ... imports module "sync" but ...src/sync/sync.v is defined as module + "main" + `.
  • Added Builder.candidate_belongs_to_foreign_project and use it in both lookup loops of find_module_path to skip candidates whose enclosing v.mod belongs to a different project than the importer, unless the candidate sits inside a configured lookup_path entry (vlib / vmodules).
  • Added a regression test in vlib/v/builder/builder_test.v that constructs a fake user project with a src/sync/ of module main files and -checks a file that imports v.ast (which transitively imports sync via shared).

Test plan

  • Reproduces the original failure with the unfixed binary: v -d trace_same_results -stats src/uptime/uptime_nix_test.v inside a fresh vlang/coreutils clone.
  • Fixed binary no longer emits any bad module definition error for that command.
  • ./v self succeeds and a second self-host pass succeeds.
  • New test_vlib_import_does_not_match_user_project_dir test in vlib/v/builder/builder_test.v fails on master and passes with this change.
  • Existing vlib/v/builder/base_url_test.v still passes; the vlib/v/checker/tests/modules/ checker fixtures still resolve their imports.

…27151)

When V is invoked from inside another project (e.g. coreutils) that
contains a sibling directory whose name matches a vlib module (like
`src/sync/`), `find_module_path` would pick that user directory before
trying vlib, and parsing then failed with "bad module definition"
because the user's files declare `module main`.

Skip candidate directories whose enclosing `v.mod` belongs to a
different project than the importer, unless the candidate sits inside a
configured `lookup_path` entry (vlib / vmodules).

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f2a4dfc70b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with πŸ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v/builder/builder.v
Comment on lines +833 to +837
for lookup in b.pref.lookup_path {
abs_lookup := os.real_path(lookup)
if abs_candidate == abs_lookup
|| abs_candidate.starts_with(abs_lookup + os.path_separator) {
return false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not classify project modules/ deps as foreign

candidate_belongs_to_foreign_project exempts only pref.lookup_path, but module resolution also searches non-lookup roots like <project>/modules (added by set_module_lookup_paths in vlib/v/builder/compile.v). When a dependency in modules/<name>/ has its own v.mod (common for vendored VPM packages), get_by_folder reports that nested v.mod root, which differs from the importer root, so this code marks it as foreign and skips it. That regresses valid imports that previously resolved from the project's modules/ directory.

Useful? React with πŸ‘Β / πŸ‘Ž.

Compare the candidate path against the importer's `v.mod` tree directly
instead of comparing the nearest enclosing `v.mod` folders. A vendored
dependency at `<project>/modules/<name>/` carries its own `v.mod`, so
the previous check classified it as a "foreign project" and skipped it,
breaking imports from `<project>`'s own code.

Added a regression test alongside the #27151 test.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0381e5d420

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with πŸ‘.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vlib/v/builder/builder.v Outdated
Comment on lines +859 to +861
importer_vmod_folder := if vmod_file_location.vmod_file.len != 0 {
vmod_file_location.vmod_folder
} else {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Anchor foreign-project check to top-level project root

find_module_path now sets importer_vmod_folder to the nearest v.mod folder, so for files inside a vendored package (e.g. <project>/modules/foo/...) the anchor becomes <project>/modules/foo. In that context, imports of sibling vendored dependencies (<project>/modules/bar) are treated as foreign by candidate_belongs_to_foreign_project (they are outside foo and not in pref.lookup_path), so transitive vendored imports can fail with module ... not found. This is a regression introduced by the new foreign-project filter for projects that vendor multiple dependent modules under the same top-level modules/ directory.

Useful? React with πŸ‘Β / πŸ‘Ž.

Vendored sibling dependencies under <project>/modules/ each carry their
own v.mod. Using the nearest enclosing v.mod as the importer's anchor
made the foreign-project check treat sibling vendored modules as foreign
and refuse to resolve them. Walk up to the outermost enclosing v.mod
instead, so the importer is anchored to the top-level project root.

Adds a regression test where modules/foo (own v.mod) imports
modules/bar (own v.mod).
@medvednikov
medvednikov merged commit 975b313 into master May 24, 2026
0 of 179 checks passed
@JalonSolov
JalonSolov deleted the fix-27151 branch July 28, 2026 01:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant